<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>DevOps life (English)</title><description>Tristan Bessoussa’s technical blog: infrastructure, Symfony, serverless and AI coding, in English.</description><link>https://devops-life.com/</link><language>en</language><atom:link href="https://devops-life.com/en/feed.xml" rel="self" type="application/rss+xml"/><lastBuildDate>Thu, 28 Nov 2024 00:00:00 GMT</lastBuildDate><image><url>https://devops-life.com/apple-touch-icon.png</url><title>DevOps life (English)</title><link>https://devops-life.com/en/</link></image><item><title>How I Used AI to Hack My CrossFit Waitlist Problem (and Why It Changed My View on AI Development)</title><link>https://devops-life.com/blog/2024/11/28/how-i-used-ai-to-hack-my-crossfit-waiting-list/</link><guid isPermaLink="true">https://devops-life.com/blog/2024/11/28/how-i-used-ai-to-hack-my-crossfit-waiting-list/</guid><description>As a CTO who barely codes anymore, I never thought I&apos;d build another app from scratch - until my CrossFit addiction and AI joined forces. Here&apos;s how I used…</description><pubDate>Thu, 28 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;As a CTO who barely codes anymore, I never thought I’d build another app from scratch - until my CrossFit addiction and AI joined forces. Here’s how I used generative AI to increase my chances of getting into popular CrossFit classes from 20% to 90%.&lt;/p&gt;
&lt;h2 id=&quot;the-problem-crossfit-class-booking-blues&quot;&gt;The Problem: CrossFit Class Booking Blues&lt;/h2&gt;
&lt;p&gt;For two years, I’ve been battling with my CrossFit box’s booking system. The classes are incredibly popular, requiring reservations up to 14 days in advance. Miss that window? Welcome to waitlist hell, often 15 people deep for the coveted after-work slots.&lt;/p&gt;
&lt;p&gt;Here’s the kicker: I’m terrible at planning ahead. My only hope was to pounce on cancellation notifications, but even my lightning-fast thumb wasn’t enough. Why? The push notifications arrived with up to a minute delay between users. Even when I clicked within a second, I’d still miss out 80% of the time.&lt;/p&gt;
&lt;h2 id=&quot;the-light-bulb-moment&quot;&gt;The Light Bulb Moment&lt;/h2&gt;
&lt;p&gt;As someone who coded for a decade before getting more hands-off, I knew there had to be a better way. The solution was clear: why wait for delayed push notifications when I could query the API directly (which is not public by the way) ? But there was a catch - while the concept was simple, the modern development landscape had evolved significantly since my coding days.&lt;/p&gt;
&lt;h2 id=&quot;enter-ai-my-unexpected-coding-buddy&quot;&gt;Enter AI: My Unexpected Coding Buddy&lt;/h2&gt;
&lt;p&gt;Last week, I decided to test all the “AI coding hype” I’d been seeing on social media. Armed with Cursor and Claude, I embarked on my diabolical plan to enhance my fitness journey.&lt;/p&gt;
&lt;p&gt;The process was fascinating:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I started small, asking AI to create an interface displaying available classes, participant counts, and waitlist numbers&lt;/li&gt;
&lt;li&gt;I extracted network calls (.HAR) from the official web app and fed them to Claude&lt;/li&gt;
&lt;li&gt;Within an hour, I had my first “wow” moment - what would have taken me 4 hours in my familiar tech stack, I accomplished in unfamiliar territory (Astro &amp;amp; React)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;the-reality-check-ai-isnt-magic-its-a-partnership&quot;&gt;The Reality Check: AI Isn’t Magic, It’s a Partnership&lt;/h2&gt;
&lt;p&gt;Working with AI wasn’t all smooth sailing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sometimes it was like working with a brilliant but distracted child&lt;/li&gt;
&lt;li&gt;It would forget context (“You generated pure React, but we’re in an Astro project, remember?”)&lt;/li&gt;
&lt;li&gt;Adding features could break existing functionality (turns out AI shares some human coding traits 😅)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The key was learning to provide context effectively, whether sharing specific files or the entire project scope to ensure coherent solutions.&lt;/p&gt;
&lt;h2 id=&quot;the-cto-perspective-a-game-changing-realization&quot;&gt;The CTO Perspective: A Game-Changing Realization&lt;/h2&gt;
&lt;p&gt;This experience transformed my view on AI tools in development. While some of my team already uses GitHub Copilot or Cursor, this personal project revealed the true potential of AI-assisted development. It’s not about replacing developers - it’s about forming an effective partnership with AI tools.&lt;/p&gt;
&lt;h2 id=&quot;key-takeaways&quot;&gt;Key Takeaways&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Clear Requirements Matter&lt;/strong&gt;: AI needs well-defined scope and expectations&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Context is King&lt;/strong&gt;: The limiting factor is often human communication - vague requests yield vague results&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Embrace Evolution&lt;/strong&gt;: Tech constantly changes; while core concepts remain, tools and frameworks evolve&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AI as an Enabler&lt;/strong&gt;: It can help experienced developers work with unfamiliar technologies more effectively.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For experienced developers, this can be a &lt;strong&gt;true game-changer&lt;/strong&gt;, enabling them to work &lt;strong&gt;XX% faster&lt;/strong&gt; even when dealing with unfamiliar technologies.&lt;/p&gt;
&lt;p&gt;For junior developers, it’s a powerful learning tool, accelerating their understanding of new technologies. However, there’s a potential downside: relying too heavily on AI could hinder their ability to develop essential skills and gain a deeper understanding of what’s happening “under the hood.”&lt;/p&gt;
&lt;p&gt;For non-technical individuals, as of 2024, it might still be a bit early to create fully functional code from scratch without a technical foundation. That said, it serves as an excellent communication aid, empowering them to convey their ideas to developers with greater clarity and precision.
At this point of time (and it could change), I don’t see AI replacing developpers, because it required me using my experience and knowledge of the&lt;/p&gt;
&lt;p&gt;P.S. Did my evil plan work? Absolutely. My success rate for getting into cancelled CrossFit slots jumped from 20% to 100%. More importantly, it freed my mind from having to constantly be prepared to jump on my phone for cancellations notifications.
Sometimes the best coding projects start with a selfish need!&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;This post was written by Tristan Bessoussa, CTO at Wamiz (Nestlé), where I am exploring ways to blend technology with practical solutions - even if it means gaming the system for a better workout! 💪&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This post was then enhanced by AI, and If you read this it until the very end, it means the AI was better at writing than me&lt;/em&gt;&lt;/p&gt;
</content:encoded><dc:creator>Tristan Bessoussa</dc:creator><dc:language>en</dc:language><category>AI</category><category>Development</category><category>Personal Project</category><category>CrossFit</category><category>Technology</category></item><item><title>A healthy development environment in 2021: hello Docker, goodbye virtual machines</title><link>https://devops-life.com/en/blog/2021/04/28/healthy-dev-environment-2021-docker-instead-of-vms/</link><guid isPermaLink="true">https://devops-life.com/en/blog/2021/04/28/healthy-dev-environment-2021-docker-instead-of-vms/</guid><description>In May 2015, Yproximité dropped its &quot;spend 1/2 a day installing everything locally by hand&quot; model in favor of a virtual machine provisioned with Ansible…</description><pubDate>Wed, 28 Apr 2021 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;background&quot;&gt;Background&lt;/h2&gt;
&lt;p&gt;In May 2015, Yproximité dropped its “spend 1/2 a day installing everything locally by hand” model in favor of a virtual machine provisioned with &lt;a href=&quot;https://www.ansible.com/&quot;&gt;Ansible&lt;/a&gt; playbooks, so that a single command would get the development environment up and running within a few minutes.&lt;/p&gt;
&lt;p&gt;To run our &lt;strong&gt;Symfony projects&lt;/strong&gt;, you had to install:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Make, to run the commands&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.virtualbox.org/wiki/Downloads&quot;&gt;VirtualBox&lt;/a&gt;, for the VM&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.vagrantup.com/downloads.html&quot;&gt;Vagrant&lt;/a&gt;, to drive the VM&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/vagrant-landrush/landrush&quot;&gt;Vagrant Landrush&lt;/a&gt;, a DNS server to reach the development URLs&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/FiloSottile/mkcert&quot;&gt;mkcert&lt;/a&gt;, to generate the local TLS certificate&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All you then had to do was run &lt;code&gt;make setup&lt;/code&gt; in your terminal: about twenty minutes later, the project was fully up and running, the database data imported and the whole team on the same minor versions of PHP, Nginx and PostgreSQL.&lt;/p&gt;
&lt;p&gt;❤️ I’d like to take this opportunity to thank the contributors to &lt;a href=&quot;https://www.manala.io/&quot;&gt;manala.io&lt;/a&gt;, a project run by my former web agency, &lt;a href=&quot;https://www.elao.com/&quot;&gt;Elao&lt;/a&gt;, and especially &lt;a href=&quot;https://github.com/nervo&quot;&gt;Nervo&lt;/a&gt;, who maintains the various &lt;a href=&quot;https://github.com/manala/ansible-roles&quot;&gt;Ansible roles&lt;/a&gt;. Thank you for your open source contributions, and for keeping our development setup running for 6 years.&lt;/p&gt;
&lt;p&gt;Fast forward to 2021. What pushed me to plan a new development setup and walk away from the virtual machine (VM) model?&lt;/p&gt;
&lt;h2 id=&quot;why-move-away-from-virtual-machines&quot;&gt;Why move away from virtual machines?&lt;/h2&gt;
&lt;p&gt;Several arguments made the case for dropping VMs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Docker keeps maturing and is well adopted by the web community for development environments.&lt;/li&gt;
&lt;li&gt;Performance lags a little behind a local install of the project (especially on Linux, if my team is to be believed).&lt;/li&gt;
&lt;li&gt;The size of the virtual machines (count on several GB per project).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Even so, I was very reluctant to adopt a development setup based solely on Docker. With a small team and no particular Docker expertise (especially on the macOS vs Linux performance issues), that choice is a risky one for the stability of the development environment, which is there to help developers be as productive as possible, not to frustrate them or slow them down.&lt;/p&gt;
&lt;p&gt;I knew VMs would soon be shown the door, but was that enough to set a deadline for moving to something else? To plan training? To move our dozens of projects to a new, Docker-based development environment? Not quite…&lt;/p&gt;
&lt;p&gt;What finally tipped the scales towards “something else” was &lt;a href=&quot;https://forums.virtualbox.org/viewtopic.php?f=8&amp;amp;t=98742#wrap&quot;&gt;a low-key announcement from the VirtualBox team&lt;/a&gt; saying they can’t technically port VirtualBox to ARM processors. VirtualBox, which runs the VMs, is a hypervisor, not a CPU emulator, so it can’t emulate an x86 processor.&lt;/p&gt;
&lt;p&gt;Why is that a problem? Well, you may have missed &lt;a href=&quot;https://nr.apple.com/dE7O5p9q0t&quot;&gt;Apple’s announcement&lt;/a&gt; of its plan to move to the ARM architecture within roughly 2 years, starting with its “M1” chip (which packs a serious punch) in the 13-inch MacBook Pro, the MacBook Air and the Mac Mini. Given the extremely positive feedback from users, the company even seems to have sped up the rollout of the new ARM architecture, announcing the 24-inch iMac and the iPad Pro with the M1 chip just a few days ago. On the consumer side, only the 16-inch MacBook Pro and the 27-inch iMac have yet to make the jump.&lt;/p&gt;
&lt;figure class=&quot;image&quot;&gt;&lt;a href=&quot;https://devops-life.com/img/vm-docker/apple-m1.jpg&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;img src=&quot;https://devops-life.com/img/vm-docker/apple-m1.jpg&quot; alt=&quot;Apple&apos;s ARM-based processor, the M1&quot; loading=&quot;lazy&quot; width=&quot;688&quot; height=&quot;442&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;figcaption&gt;Apple&apos;s ARM-based processor, the M1&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;I can see the Apple trolls coming from a mile away, but in my team I’ve always made a point of giving developers the choice between Linux and macOS (&lt;a href=&quot;https://barometre.afup.org/report/os_developpment?filter%5Bcampaign%5D%5B%5D=6&amp;amp;filter%5Bcampaign%5D%5B%5D=7&amp;amp;filter%5Bcampaign%5D%5B%5D=8&amp;amp;filter%5Bsalary%5D%5Bmin%5D=&amp;amp;filter%5Bsalary%5D%5Bmax%5D=&amp;amp;filter%5Bsubmit%5D=&quot;&gt;see the OS breakdown in the AFUP survey of French PHP developers&lt;/a&gt;). So please redirect the trolling to the only ones who deserve it: PHP/JS devs on Windows.&lt;/p&gt;
&lt;h2 id=&quot;a-docker-powered-solution-but-not-just-docker&quot;&gt;A Docker-powered solution, but not just Docker!&lt;/h2&gt;
&lt;p&gt;Given my company and my team, a Docker-only solution is a non-starter until the performance problem on Mac is fixed for good, or as long as it takes a Docker/Linux expert to implement it.&lt;/p&gt;
&lt;p&gt;So I chose to push for a hybrid solution that avoids shared volumes (and with them, the performance problems). If I remember SymfonyLive correctly, that’s also what &lt;a href=&quot;https://twitter.com/fabpot&quot;&gt;Fabien Potencier&lt;/a&gt; went for.&lt;/p&gt;
&lt;p&gt;On the host machine (macOS/Linux):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PHP and Composer&lt;/li&gt;
&lt;li&gt;NodeJS&lt;/li&gt;
&lt;li&gt;Docker Desktop&lt;/li&gt;
&lt;li&gt;Symfony CLI. It provides a web server with TLS handling, Docker support and per-project PHP versions.&lt;/li&gt;
&lt;li&gt;The Manala binary (it generates your &lt;code&gt;docker-compose.yaml&lt;/code&gt; from a template; see &lt;a href=&quot;#aller-plus-loin&quot;&gt;going further&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;⚠ For all its benefits, Symfony CLI is not open source. Worth keeping in mind, especially if you rely on it for your development setup.&lt;/p&gt;
&lt;p&gt;In Docker:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Database (MySQL, PostgreSQL, MariaDB…)&lt;/li&gt;
&lt;li&gt;Redis server&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;image&quot;&gt;&lt;a href=&quot;https://devops-life.com/img/vm-docker/docker-containers.jpg&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;img src=&quot;https://devops-life.com/img/vm-docker/docker-containers.jpg&quot; alt=&quot;List of Docker containers&quot; loading=&quot;lazy&quot; width=&quot;688&quot; height=&quot;411&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;figcaption&gt;List of Docker containers&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Developing on this new hybrid stack is genuinely pleasant and fast: I get my machine’s native performance back.&lt;/p&gt;
&lt;h2 id=&quot;going-further&quot;&gt;&lt;span id=&quot;aller-plus-loin&quot;&gt;&lt;/span&gt;Going further&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/HugoAlliaume&quot;&gt;Hugo Alliaume&lt;/a&gt; took over the POC I built in August 2020 and cleared every blocker. He tackled the problem head-on and saw the transition through across all of the R&amp;amp;D team’s projects.&lt;/p&gt;
&lt;p&gt;He covers the more technical parts on his blog, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A deeper look at the limits of VMs and the workarounds they require&lt;/li&gt;
&lt;li&gt;Why replace your web server / reverse proxy and local DNS with Symfony CLI&lt;/li&gt;
&lt;li&gt;An example &lt;code&gt;docker-compose.yaml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;How to make the most of this development stack in CI, with GitHub Actions&lt;/li&gt;
&lt;li&gt;How to go further with the &lt;code&gt;manala&lt;/code&gt; binary to make management and maintenance easier (e.g. &lt;code&gt;Makefile&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;📖  I recommend reading his post (in French): &lt;a href=&quot;https://hugo.alliau.me/2021/04/26/migration-stack-developpement/&quot;&gt;migrating our development stack to Docker&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;installing-the-prerequisites-on-macos&quot;&gt;Installing the prerequisites on macOS&lt;/h2&gt;
&lt;p&gt;This section is for macOS users 🍎 who’d like to find all the steps for installing the tools on their machine in one place.&lt;/p&gt;
&lt;h3 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h3&gt;
&lt;p&gt;Install &lt;a href=&quot;https://brew.sh/index_fr&quot;&gt;&lt;code&gt;brew&lt;/code&gt;&lt;/a&gt;, the package manager for macOS.&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes devops-life-light devops-life-dark&quot; style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC;--shiki-light-bg:#FFFFFF;--shiki-dark-bg:#151C26; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;/bin/bash&lt;/span&gt;&lt;span style=&quot;--shiki-light:#AE3F14;--shiki-dark:#F3996F&quot;&gt; -c&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; &quot;$(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;curl&lt;/span&gt;&lt;span style=&quot;--shiki-light:#AE3F14;--shiki-dark:#F3996F&quot;&gt; -fsSL&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;)&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Install &lt;a href=&quot;https://ohmyz.sh/#install&quot;&gt;&lt;code&gt;Zsh&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes devops-life-light devops-life-dark&quot; style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC;--shiki-light-bg:#FFFFFF;--shiki-dark-bg:#151C26; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;sh&lt;/span&gt;&lt;span style=&quot;--shiki-light:#AE3F14;--shiki-dark:#F3996F&quot;&gt; -c&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; &quot;$(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;curl&lt;/span&gt;&lt;span style=&quot;--shiki-light:#AE3F14;--shiki-dark:#F3996F&quot;&gt; -fsSL&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;)&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;installing-php&quot;&gt;Installing PHP&lt;/h3&gt;
&lt;pre class=&quot;astro-code astro-code-themes devops-life-light devops-life-dark&quot; style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC;--shiki-light-bg:#FFFFFF;--shiki-dark-bg:#151C26; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;brew&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; php&lt;/span&gt;&lt;span style=&quot;--shiki-light:#5F6B7D;--shiki-light-font-style:italic;--shiki-dark:#8795A9;--shiki-dark-font-style:italic&quot;&gt; # installe la dernière version stable de PHP, soit la version 8.0.x à l&apos;heure ou j&apos;écris cet article.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;brew&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; php@7.4&lt;/span&gt;&lt;span style=&quot;--shiki-light:#5F6B7D;--shiki-light-font-style:italic;--shiki-dark:#8795A9;--shiki-dark-font-style:italic&quot;&gt; # installe php 7.4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;brew&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; composer&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If your projects run on different PHP versions, no problem: install every PHP version they need, and Symfony will use the right one thanks to the &lt;code&gt;.php-version&lt;/code&gt; file at the root of your project.&lt;/p&gt;
&lt;figure class=&quot;image&quot;&gt;&lt;a href=&quot;https://devops-life.com/img/vm-docker/symfony-cli-php-version.jpg&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;img src=&quot;https://devops-life.com/img/vm-docker/symfony-cli-php-version.jpg&quot; alt=&quot;The PHP versions installed on your machine, as detected by the Symfony binary&quot; loading=&quot;lazy&quot; width=&quot;688&quot; height=&quot;347&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;figcaption&gt;The PHP versions installed on your machine, as detected by the Symfony binary&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h3 id=&quot;installing-nodejs&quot;&gt;Installing NodeJS&lt;/h3&gt;
&lt;pre class=&quot;astro-code astro-code-themes devops-life-light devops-life-dark&quot; style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC;--shiki-light-bg:#FFFFFF;--shiki-dark-bg:#151C26; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;brew&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; node&lt;/span&gt;&lt;span style=&quot;--shiki-light:#5F6B7D;--shiki-light-font-style:italic;--shiki-dark:#8795A9;--shiki-dark-font-style:italic&quot;&gt; # installe la dernière version stable de NodeJS, soit la version 16.0.x à l&apos;heure ou j&apos;écris cet article.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;brew&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; node@14&lt;/span&gt;&lt;span style=&quot;--shiki-light:#5F6B7D;--shiki-light-font-style:italic;--shiki-dark:#8795A9;--shiki-dark-font-style:italic&quot;&gt; # installe la nodeJS 14&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;brew&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; yarn&lt;/span&gt;&lt;span style=&quot;--shiki-light:#5F6B7D;--shiki-light-font-style:italic;--shiki-dark:#8795A9;--shiki-dark-font-style:italic&quot;&gt; # si vous avez besoin du gestionnaire de dépandance JS Yarn&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Same idea for NodeJS: if your projects run on different versions, install every version they need. We’ll use &lt;code&gt;nvm&lt;/code&gt;, which picks the right Node version for each project (the version lives in the &lt;code&gt;.nvmrc&lt;/code&gt; file at the project root).&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes devops-life-light devops-life-dark&quot; style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC;--shiki-light-bg:#FFFFFF;--shiki-dark-bg:#151C26; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;brew&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; nvm&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, so you don’t have to run &lt;code&gt;nvm use&lt;/code&gt; to select the right NodeJS version before your &lt;code&gt;yarn install&lt;/code&gt; or &lt;code&gt;yarn dev-server&lt;/code&gt;, you can add this block to your &lt;code&gt;~/.zshrc&lt;/code&gt; (then restart your terminal, or run &lt;code&gt;source ~/.zshrc&lt;/code&gt;). It checks whether your current directory has a &lt;code&gt;.nvmrc&lt;/code&gt; with a project-specific Node version, and uses that version if it’s installed.&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes devops-life-light devops-life-dark&quot; style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC;--shiki-light-bg:#FFFFFF;--shiki-dark-bg:#151C26; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC&quot;&gt; NVM_DIR&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC&quot;&gt;$HOME&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;/.nvm&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; -s&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;/usr/local/opt/nvm/nvm.sh&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; ]&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; &amp;amp;&amp;amp;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt; .&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;/usr/local/opt/nvm/nvm.sh&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#5F6B7D;--shiki-light-font-style:italic;--shiki-dark:#8795A9;--shiki-dark-font-style:italic&quot;&gt;  # This loads nvm&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; -s&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;/usr/local/opt/nvm/etc/bash_completion.d/nvm&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; ]&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; &amp;amp;&amp;amp;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt; .&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;/usr/local/opt/nvm/etc/bash_completion.d/nvm&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#5F6B7D;--shiki-light-font-style:italic;--shiki-dark:#8795A9;--shiki-dark-font-style:italic&quot;&gt;  # This loads nvm bash_completion&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#5F6B7D;--shiki-light-font-style:italic;--shiki-dark:#8795A9;--shiki-dark-font-style:italic&quot;&gt;# place this after nvm initialization!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;autoload&lt;/span&gt;&lt;span style=&quot;--shiki-light:#AE3F14;--shiki-dark:#F3996F&quot;&gt; -U&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; add-zsh-hook&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;load-nvmrc&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt;  local&lt;/span&gt;&lt;span style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC&quot;&gt; node_version&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&quot;$(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;nvm&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; version&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt;  local&lt;/span&gt;&lt;span style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC&quot;&gt; nvmrc_path&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&quot;$(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;nvm_find_nvmrc&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt;  if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; -n&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC&quot;&gt;$nvmrc_path&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; ];&lt;/span&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt;    local&lt;/span&gt;&lt;span style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC&quot;&gt; nvmrc_node_version&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;=$(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;nvm&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; version&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; &quot;$(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;cat&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; &quot;${&lt;/span&gt;&lt;span style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC&quot;&gt;nvmrc_path&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;}&quot;)&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC&quot;&gt;$nvmrc_node_version&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;N/A&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; ];&lt;/span&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;      nvm&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; install&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt;    elif&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC&quot;&gt;$nvmrc_node_version&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; !=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC&quot;&gt;$node_version&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; ];&lt;/span&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;      nvm&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; use&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt;    fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt;  elif&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC&quot;&gt;$node_version&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; !=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; &quot;$(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;nvm&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; version default&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;)&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; ];&lt;/span&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;    echo&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;Reverting to nvm default version&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;    nvm&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; use&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; default&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt;  fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;add-zsh-hook&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; chpwd&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; load-nvmrc&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;load-nvmrc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;bonus-mux-and-tmuxinator&quot;&gt;Bonus: mux and tmuxinator&lt;/h3&gt;
&lt;p&gt;If you’re working on a Symfony project with JS that needs compiling, you’ll probably need 3 terminal tabs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One session running &lt;code&gt;symfony serve&lt;/code&gt;, the web server&lt;/li&gt;
&lt;li&gt;One session at the root of your project&lt;/li&gt;
&lt;li&gt;One session running &lt;code&gt;yarn dev-server&lt;/code&gt;, to compile your JavaScript whenever it changes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For extra comfort, I suggest using &lt;a href=&quot;https://iterm2.com/&quot;&gt;iTerm2&lt;/a&gt; with the &lt;code&gt;tmux&lt;/code&gt; terminal multiplexer. It lets you split a terminal window into several virtual terminals. In practice, when I run &lt;code&gt;mux start &amp;lt;nom_du_projet&amp;gt;&lt;/code&gt; in my terminal, my containers start automatically, my terminal gets split, and &lt;code&gt;yarn dev-server&lt;/code&gt; runs in a virtual tab, as shown below.&lt;/p&gt;
&lt;figure class=&quot;image&quot;&gt;&lt;a href=&quot;https://devops-life.com/img/vm-docker/symfony-cli.jpg&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;img src=&quot;https://devops-life.com/img/vm-docker/symfony-cli.jpg&quot; alt=&quot;The Symfony CLI binary at work: on the left, the various steps to boot the project; on the right, the web server started by `symfony serve`, which will handle your requests&quot; loading=&quot;lazy&quot; width=&quot;688&quot; height=&quot;396&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;figcaption&gt;The Symfony CLI binary at work: on the left, the various steps to boot the project; on the right, the web server started by `symfony serve`, which will handle your requests&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;To get the same setup:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes devops-life-light devops-life-dark&quot; style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC;--shiki-light-bg:#FFFFFF;--shiki-dark-bg:#151C26; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;brew&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; tmux&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;brew&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; tmuxinator&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then add the alias to your &lt;code&gt;~/.zshrc&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes devops-life-light devops-life-dark&quot; style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC;--shiki-light-bg:#FFFFFF;--shiki-dark-bg:#151C26; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;alias mux=tmuxinator&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Setting up a project is then pretty basic (with your project’s name in place of &lt;code&gt;&amp;lt;nom_du_projet&amp;gt;&lt;/code&gt;):&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes devops-life-light devops-life-dark&quot; style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC;--shiki-light-bg:#FFFFFF;--shiki-dark-bg:#151C26; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;mux open &amp;lt;nom_du_projet&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then configure your project using the &lt;a href=&quot;https://github.com/tmuxinator/tmuxinator&quot;&gt;tmuxinator&lt;/a&gt; documentation, for example:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes devops-life-light devops-life-dark&quot; style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC;--shiki-light-bg:#FFFFFF;--shiki-dark-bg:#151C26; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#5F6B7D;--shiki-light-font-style:italic;--shiki-dark:#8795A9;--shiki-dark-font-style:italic&quot;&gt;# /Users/t.bessoussa/.config/tmuxinator/&amp;lt;nom_du_projet&amp;gt;.yml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; &amp;lt;nom_du_projet&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt;root&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; ~/workspace/&amp;lt;mon_projet&amp;gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#5F6B7D;--shiki-light-font-style:italic;--shiki-dark:#8795A9;--shiki-dark-font-style:italic&quot;&gt; # Chemin de votre projet&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#5F6B7D;--shiki-light-font-style:italic;--shiki-dark:#8795A9;--shiki-dark-font-style:italic&quot;&gt;# Run on project exit ( detaching from tmux session )&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt;on_project_exit&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; make halt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#5F6B7D;--shiki-light-font-style:italic;--shiki-dark:#8795A9;--shiki-dark-font-style:italic&quot;&gt;# Run on project stop&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt;on_project_stop&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; make halt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt;windows&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;  -&lt;/span&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt; php&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt;      layout&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; main-vertical&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt;      panes&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;        -&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; make setup&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;        -&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; symfony serve&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;  -&lt;/span&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt; js&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;    -&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; sleep 70&lt;/span&gt;&lt;span style=&quot;--shiki-light:#5F6B7D;--shiki-light-font-style:italic;--shiki-dark:#8795A9;--shiki-dark-font-style:italic&quot;&gt; # on attends un peu vu que mon make setup va lancer un build js de dev qui rentrerait en conflit avec le dev-server&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;    -&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; yarn dev-server&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All that’s left is to remember &lt;code&gt;mux start &amp;lt;nom_du_projet&amp;gt;&lt;/code&gt; and &lt;code&gt;mux stop &amp;lt;nom_du_projet&amp;gt;&lt;/code&gt;, and you’re all set.&lt;/p&gt;
&lt;p&gt;Spotted a typo? The original post (in French) is on my &lt;a href=&quot;https://github.com/tristanbes/devops-life/blob/gh-pages/_posts/2021-04-28-un-environnement-de-developpement-sain-en-2021-hello-docker-goodbye-machines-virtuelles.md&quot;&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;
</content:encoded><dc:creator>Tristan Bessoussa</dc:creator><dc:language>en</dc:language><category>VM</category><category>Docker</category><category>Machine Virtuelles</category><category>PHP</category><category>environnement de développement</category></item><item><title>Test-driving Hidora, a Swiss PaaS</title><link>https://devops-life.com/en/blog/2021/01/19/swiss-paas-hidora-review/</link><guid isPermaLink="true">https://devops-life.com/en/blog/2021/01/19/swiss-paas-hidora-review/</guid><description>This post follows on from a series sharing hands-on feedback on French PaaS providers, with one problem in mind: hosting a fleet of 120+ WordPress sites.</description><pubDate>Tue, 19 Jan 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This post follows on from a series sharing hands-on feedback on French PaaS providers, with one problem in mind: hosting a fleet of 120+ WordPress sites.&lt;/p&gt;
&lt;p&gt;In the original series:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Part 1. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/19/french-paas-for-wordpress-comparison/&quot;&gt;Background to the French PaaS comparison&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 2. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/20/ovh-cloud-web-paas-review/&quot;&gt;PaaS comparison: OVH falls overboard&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 3. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/21/clever-cloud-paas-review/&quot;&gt;PaaS comparison: Clever Cloud, &lt;del&gt;the boat is taking on water&lt;/del&gt; the captain rights the ship&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 4. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/22/scalingo-paas-review/&quot;&gt;PaaS comparison: Scalingo stays afloat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 5. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/23/platform-sh-paas-review/&quot;&gt;PaaS comparison: Platform.sh lies at anchor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 6. &lt;a href=&quot;https://devops-life.com/en/blog/2021/01/19/swiss-paas-hidora-review/&quot;&gt;PaaS comparison: Hidora, the Swiss cloud&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 7. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/24/clever-cloud-vs-scalingo/&quot;&gt;PaaS comparison: performance, Clever Cloud VS Scalingo VS Hidora&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Although my quest to host our WordPress fleet came to an end in late 2019, I decided to try out Hidora, a Swiss PaaS 🇨🇭.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;hidora&quot;&gt;Hidora&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;#r%C3%A9sum%C3%A9-hidora-tldr&quot;&gt;Skip to the summary if you’re in a hurry&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;building-the-application-on-hidora&quot;&gt;Building the application on Hidora&lt;/h4&gt;
&lt;p&gt;Hidora offers prebuilt installs for the main CMSs. However, you can’t choose which edition of WordPress you want: the only one on offer is the basic edition, not &lt;a href=&quot;https://roots.io/bedrock/&quot;&gt;Bedrock&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So how do we build our WordPress, which needs its &lt;code&gt;composer&lt;/code&gt; and &lt;code&gt;javascript&lt;/code&gt; dependencies installed?&lt;/p&gt;
&lt;p&gt;We ran into several difficulties getting our WordPress (Bedrock) project deployed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Hidora’s PHP environments don’t ship with any &lt;code&gt;Node.js&lt;/code&gt; runtime, so you can’t run &lt;code&gt;yarn install &amp;amp;&amp;amp; yarn build&lt;/code&gt; there to generate the &lt;code&gt;css&lt;/code&gt; and &lt;code&gt;js&lt;/code&gt; files. We had to identify which distribution the scaler runs on (CentOS), then install &lt;code&gt;Node.js&lt;/code&gt; and then &lt;code&gt;yarn&lt;/code&gt; by hand on the PHP node over SSH, after first asking support to give us &lt;code&gt;root&lt;/code&gt; access to our environment.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When customizing the “post deploy” script to add our &lt;code&gt;composer install&lt;/code&gt;, we expected to be at the root of the folder containing our deployed application. We weren’t. It wasn’t easy to figure out where to go to get into our application directory (&lt;code&gt;cd $WEBROOT/app&lt;/code&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Unfortunately, there’s no bulk editing of environment variables. I had to enter 30 of them by hand, one by one: it takes ages. An editable text area where we could paste all our variables would have been quicker.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I think the frustration caused by all these problems and questions could be solved by publishing “cookbooks” that answer the questions new customers are bound to ask when they arrive on Hidora (e.g. how do I deploy a Symfony or WordPress application…). The only guides I found were those for the platform Hidora chose to run its cloud: &lt;a href=&quot;https://jelastic.com/&quot;&gt;Jelastic&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Screenshot of the admin interface:
&lt;img src=&quot;https://devops-life.com/img/paas/hidora-ui.png&quot; alt=&quot;Hidora admin interface&quot; width=&quot;1305&quot; height=&quot;884&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;performance-at-hidora&quot;&gt;Performance at Hidora&lt;/h4&gt;
&lt;p&gt;Here’s one of the infrastructure topologies running the WordPress site. The visuals make it easier to understand the setup and the resources allocated.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://devops-life.com/img/paas/hidora-typology.png&quot; alt=&quot;Hidora WordPress infrastructure topology&quot; width=&quot;979&quot; height=&quot;609&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot;&gt;&lt;/p&gt;
&lt;p&gt;Example: I send 5 unique visitors per second to the home page for 2 minutes. No optimizations, except for &lt;code&gt;pm.max_children&lt;/code&gt; set to 10.&lt;/p&gt;
&lt;p&gt;For &lt;strong&gt;€10.8&lt;/strong&gt;/month at &lt;u&gt;Scalingo&lt;/u&gt;, I get an average response time of &lt;strong&gt;492ms&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;For &lt;strong&gt;€15.7&lt;/strong&gt;/month at &lt;u&gt;Hidora&lt;/u&gt;, I get an average response time of &lt;strong&gt;2,048ms&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;So performance isn’t good out of the box. I think better performance would be possible, but since I didn’t have to apply any other improvements on the other platforms I tested, I didn’t do it here either.&lt;/p&gt;
&lt;p&gt;For more details, see:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Part 7. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/24/clever-cloud-vs-scalingo/&quot;&gt;PaaS comparison: performance, Clever Cloud VS Scalingo VS Hidora&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that Hidora bills for the resources you actually consume. We can reserve 2GHz of CPU and 2GB of RAM, and allow consumption of up to 6GHz and 6GB of RAM. If your application sometimes uses 3.4GHz of CPU, you’re only billed for that consumption, not for the maximum you chose (6GHz), which helps keep costs under control during load spikes.&lt;/p&gt;
&lt;h3 id=&quot;hidora-summary-tldr&quot;&gt;&lt;span id=&quot;résumé-hidora-tldr&quot;&gt;&lt;/span&gt;Hidora summary (TL;DR)&lt;/h3&gt;
&lt;h4 id=&quot;-cons&quot;&gt;👎 Cons&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Interface based on Jelastic: it’s complex and not very sexy.&lt;/li&gt;
&lt;li&gt;The Jelastic interface could have been more UX-oriented.&lt;/li&gt;
&lt;li&gt;The flexibility can lead to bad practices (= editing config files by hand on the servers)&lt;/li&gt;
&lt;li&gt;Not really a 100% “PaaS”: you end up installing things manually over &lt;code&gt;SSH&lt;/code&gt; with &lt;code&gt;apt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The application is built on the same machine that runs the code. I had to increase the platform’s available RAM just for the build, even though the run didn’t really need it.&lt;/li&gt;
&lt;li&gt;No easy (1-click) way to offload the application build to a separate, bigger scaler so you only pay for that scaler while you actually need it.&lt;/li&gt;
&lt;li&gt;Weak out-of-the-box performance (without any optimization work)&lt;/li&gt;
&lt;li&gt;Support doesn’t offer a “live chat” channel&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;-pros&quot;&gt;👍 Pros&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Interface based on Jelastic: in-house resources go to something other than building the UI.&lt;/li&gt;
&lt;li&gt;Very flexible (&lt;code&gt;root&lt;/code&gt;, &lt;code&gt;sFTP&lt;/code&gt;, &lt;code&gt;ssh&lt;/code&gt; access). That’s great if you have lots of integrations that other PaaS providers don’t support.&lt;/li&gt;
&lt;li&gt;Lots of supported technologies, including Docker&lt;/li&gt;
&lt;li&gt;Support took the time to work through some of our issues over video call, with screen sharing.&lt;/li&gt;
&lt;li&gt;The data is hosted in Switzerland.&lt;/li&gt;
&lt;li&gt;With flexible resources, you’re billed for what you actually consume.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;verdict&quot;&gt;Verdict&lt;/h3&gt;
&lt;p&gt;I’m not sure I can recommend Hidora in a scenario where a &lt;strong&gt;developer&lt;/strong&gt; is in charge of configuring and deploying an application (as can be the case in small teams). The UI layer provided by Jelastic is, in my view, fairly hard to get to grips with, and the experience calls for more “infra / ops / run” skills than the other 100% PaaS hosts I’ve covered in this series.&lt;/p&gt;
&lt;p&gt;You can end up doing a hybrid of operations: configuration through the UI, as you’d expect from a PaaS, but also, more surprisingly, editing configuration files (&lt;code&gt;php.ini&lt;/code&gt;, &lt;code&gt;vhost.conf&lt;/code&gt;) over &lt;code&gt;sFTP&lt;/code&gt; or &lt;code&gt;SSH&lt;/code&gt;, which feels like configuring a dedicated server or a VPS.&lt;/p&gt;
&lt;p&gt;For customers discovering the Jelastic interface that Hidora uses, a screen-sharing session with support is a must to make sense of it all.&lt;/p&gt;
&lt;p&gt;⚠️ My opinion of this host only covers the way I used it. Honestly, I’m not sure my use case gets the most out of this host ⚠️&lt;/p&gt;
</content:encoded><dc:creator>Tristan Bessoussa</dc:creator><dc:language>en</dc:language><category>PaaS</category><category>Performances</category><category>Hidora</category><category>Hosting</category><category>Cloud</category></item><item><title>How Serverless Saved Us For $2.</title><link>https://devops-life.com/blog/2020/03/06/how-serverless-saved-us-for-%242-with-bref-sh/</link><guid isPermaLink="true">https://devops-life.com/blog/2020/03/06/how-serverless-saved-us-for-$2-with-bref-sh/</guid><description>You&apos;ve probably heard or seen words &quot;serverless&quot; or &quot;FaaS&quot; -Function as a Service- over the past few months asking yourself, &quot;ok, is this hype or does it…</description><pubDate>Fri, 06 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;You’ve probably heard or seen words “serverless” or “FaaS” -Function as a Service- over the past few months asking yourself, “ok, is this hype or does it actually have some benefits” ?&lt;/p&gt;
&lt;p&gt;Well, I’m going to share a feedback for our use case at my job that will illustrate why going serverless helped us achieved what was asked.&lt;/p&gt;
&lt;p&gt;To achieve our goal we used the following products/framework/libraries:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://symfony.com/&quot;&gt;Symfony (PHP)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bref.sh/&quot;&gt;Bref.sh&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://serverless.com/&quot;&gt;Serverless framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/spatie/browsershot&quot;&gt;Browsershot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pptr.dev/&quot;&gt;Puppeteer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/sqs/&quot;&gt;Amazon SQS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/lambda/?nc2=type_a&quot;&gt;Amazon Lambda&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.scaleway.com/en/object-storage/&quot;&gt;Scaleway S3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My team works with &lt;code&gt;PHP7&lt;/code&gt;, &lt;code&gt;Symfony&lt;/code&gt; and &lt;code&gt;Vue.js&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can contribute to this article for any typos or errors you may find by &lt;a href=&quot;https://github.com/tristanbes/devops-life/tree/gh-pages/_posts/2020-03-06-how-serverless-saved-us-for-$2-with-bref-sh.md&quot;&gt;submitting a pull request&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-need-generate-pdf-analytics-reports&quot;&gt;The need: Generate PDF analytics reports&lt;/h2&gt;
&lt;p&gt;Here’s the user stories:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“As a customer of the CMS,
I should receive each month a PDF report containing serveral analytics for my website”&lt;/li&gt;
&lt;li&gt;“As a reseller of this group of customer of the CMS,
I should receive each month a PDF report of each customer’s reports (containing a part of each)”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In two sentences, for our few customers with this feature, we’ll have to generate 2,000+ PDFs.
Here’s what it looks like for one of the PDFs we have to generate:&lt;/p&gt;
&lt;figure class=&quot;image&quot;&gt;&lt;a href=&quot;https://devops-life.com/img/serverless/pdf-lambda-result.jpg&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;img src=&quot;https://devops-life.com/img/serverless/pdf-lambda-result.jpg&quot; alt=&quot;PDF result&quot; loading=&quot;lazy&quot; width=&quot;688&quot; height=&quot;842&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;figcaption&gt;PDF result&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h3 id=&quot;vps--dedicated-server-option&quot;&gt;VPS / Dedicated server option&lt;/h3&gt;
&lt;p&gt;You could setup a cron job on one of your servers and it will take care of launching a command to generate the PDF.
The main problem: &lt;strong&gt;it doesn’t scale&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;~20 seconds (pdf generation) x 2,000 PDF = &lt;em&gt;11 hours of PDF generation&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Average of generation is 2 seconds, but on a heavy PDF that contains multiple pages of multiple reports it can require up to 30 seconds.&lt;/p&gt;
&lt;p&gt;During those 11 hours you’d need to ensure that the generation can survive that long (through app deployements, server restart etc…) and a way to monitor that the process is running, for example using &lt;a href=&quot;http://supervisord.org/&quot;&gt;Supervisor&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It would also require you to code the whole process using “&lt;a href=&quot;https://en.wikipedia.org/wiki/Defensive_programming&quot;&gt;defensive programming&lt;/a&gt;” in order to ensure the continuing function of your code under unforeseen circumstances.&lt;/p&gt;
&lt;p&gt;Since we run a PHP app, it’s not multi-threaded, meaning, we can’t easily take advantage from all the CPU cores of our dedicated server. One of the options, if we want to achieve concurrency, is to rent more servers which we have to deploy, provision and manage.&lt;/p&gt;
&lt;h3 id=&quot;serverless-option&quot;&gt;Serverless option&lt;/h3&gt;
&lt;p&gt;Here’s the workflow of our setup.&lt;/p&gt;
&lt;figure class=&quot;image&quot;&gt;&lt;a href=&quot;https://devops-life.com/img/serverless/pdf-lambda-architecture.jpg&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;img src=&quot;https://devops-life.com/img/serverless/pdf-lambda-architecture.jpg&quot; alt=&quot;PDF generation serverless architecture with 2 Symfony apps. Full HD image for the serverless architecture .&quot; loading=&quot;lazy&quot; width=&quot;688&quot; height=&quot;310&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;figcaption&gt;PDF generation serverless architecture with 2 Symfony apps. &lt;br /&gt;&lt;a href=&apos;/img/serverless/pdf-lambda-architecture.jpg&apos;&gt;Full HD image for the serverless architecture&lt;/a&gt;.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;figure class=&quot;image&quot;&gt;&lt;a href=&quot;https://devops-life.com/img/serverless/puml-lambda-pdf.png&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;img src=&quot;https://devops-life.com/img/serverless/puml-lambda-pdf.png&quot; alt=&quot;PUML diagram of the PDF generation workflow&quot; loading=&quot;lazy&quot; width=&quot;688&quot; height=&quot;310&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;figcaption&gt;PUML diagram of the PDF generation workflow&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h4 id=&quot;step-1-preparing-the-data&quot;&gt;Step 1: preparing the data&lt;/h4&gt;
&lt;p&gt;Since this PDF is an aggregation of multiple external and internal data, you will want to have the &lt;strong&gt;data ready&lt;/strong&gt; when you’ll generate the PDF. The CMS application (&lt;code&gt;app #1&lt;/code&gt;, PHP/Symfony) is responsible of &lt;strong&gt;gathering and storing all the data&lt;/strong&gt; that is necessary to build the PDF into a Redis instance.&lt;/p&gt;
&lt;p&gt;Since we want to achieve scalability, you don’t want the service responsible of generating the PDF to ask via an API the data to the &lt;code&gt;app #1&lt;/code&gt; because it would become a bottleneck if 3,000 simultaneous lambda would send requests to your API.&lt;/p&gt;
&lt;h4 id=&quot;step-2-create-async-messages&quot;&gt;Step 2: create async messages&lt;/h4&gt;
&lt;p&gt;Now that all the data is ready, the &lt;code&gt;app #1&lt;/code&gt; is crafting the data in a message, wrapping it inside an enveloppe using &lt;a href=&quot;https://symfony.com/doc/current/messenger.html&quot;&gt;Symfony Messenger&lt;/a&gt; in order to &lt;strong&gt;send the messages to an Amazon SQS queue&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Note for Symfony users: because the messages will be read by another (Symfony) application, you must use a custom message serializer. You can read more on a &lt;a href=&quot;https://github.com/symfony/symfony/issues/33912&quot;&gt;RFC I opened on Symfony repository&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;step-3-store-messages-in-a-queue&quot;&gt;Step 3: store messages in a queue&lt;/h4&gt;
&lt;p&gt;As soon as the messages arrive inside an Amazon SQS queue, it triggers an action on our lambda running our code to generate PDFs.&lt;/p&gt;
&lt;p&gt;Note: our message structure (Plain Old PHP Object) is stored in another git repository. Why ? because both &lt;code&gt;app #1&lt;/code&gt; and &lt;code&gt;app #2&lt;/code&gt; need to have this POPO, so chances that a developper would update the message structure on &lt;code&gt;app #1&lt;/code&gt; but not on &lt;code&gt;app #2&lt;/code&gt; were an unecessary risk.&lt;/p&gt;
&lt;h4 id=&quot;step-4-generate-the-pdfs&quot;&gt;Step 4: generate the PDFs&lt;/h4&gt;
&lt;p&gt;Up to &lt;strong&gt;3,000 lambda instances are started&lt;/strong&gt;, that’s some serious scalability there (in our case we limited it at 50 concurrent lambdas). Each lambda receives one message containing the data to generate 1 PDF.&lt;/p&gt;
&lt;p&gt;Then the lambda runs our &lt;code&gt;app #2&lt;/code&gt; (PHP, Symfony) responsible of rendering a view inside a web page using a simple &lt;code&gt;.twig&lt;/code&gt; templates with some javascript to render charts.&lt;/p&gt;
&lt;p&gt;Then the code uses &lt;a href=&quot;https://github.com/spatie/browsershot&quot;&gt;Browsershot&lt;/a&gt;, which transforms a web page to a PDF by using a headless Chrome controlled via pupetter to visit the URL that renders the &lt;code&gt;.twig&lt;/code&gt; template which will become our PDF.&lt;/p&gt;
&lt;p&gt;Once the PDF is generated, it’s stored in a &lt;strong&gt;S3 bucket&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;We used &lt;a href=&quot;https://bref.sh/&quot;&gt;Bref.sh&lt;/a&gt; around Symfony; It’s &lt;strong&gt;awesome&lt;/strong&gt; because it provides lambda integration for your favorite PHP framework and also different &lt;a href=&quot;https://bref.sh/docs/runtimes/#bref-runtimes&quot;&gt;PHP runtime&lt;/a&gt; that will run on the lambda.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;All the 2,000 PDFs are generated within 2 minutes&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id=&quot;step-5-email-the-pdf-to-the-client&quot;&gt;Step 5: email the PDF to the client&lt;/h4&gt;
&lt;p&gt;The architecture image is only a part of what really happens. Because once the PDF has been generated in the bucket, we generate a new Message, send it to another SQS queue, and then, this particular queue is using &lt;code&gt;app #2&lt;/code&gt; again (but another part of the code) to send an email to the client with the PDF attached and notify the CMS application so clients can download their PDF through the backoffice too.&lt;/p&gt;
&lt;h2 id=&quot;comparison&quot;&gt;Comparison&lt;/h2&gt;
&lt;div class=&quot;table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Table&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;VPS or Dedicated server&lt;/th&gt;
&lt;th&gt;Serverless&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Complexity&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;Expensive&lt;/td&gt;
&lt;td&gt;Cheap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scalability&lt;/td&gt;
&lt;td&gt;Complex&lt;/td&gt;
&lt;td&gt;Very scalable&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;hr&gt;
&lt;p&gt;One of the main advantage of using serverless architecture is that you get billed only for the time it was required to run the code.&lt;/p&gt;
&lt;p&gt;You also don’t have to take care of where or how it runs. You just ship code and it runs in the cloud. The cloud provider is responsible of managing the allocation of the machine resources.&lt;/p&gt;
&lt;p&gt;In our example, using the free tier of Amazon Lambda and Amazon SQS, generating 2,000 PDFs is costing us &lt;strong&gt;~$1.63&lt;/strong&gt; ($1.62 for SQS, $0.01 for Lambda). You can use this &lt;a href=&quot;https://cost-calculator.bref.sh/&quot;&gt;serverless costs calculator&lt;/a&gt; to get better insights.&lt;/p&gt;
&lt;p&gt;If you think you have some part of your code that could benefit from being moved to a serverless architecture, I would advise that you start with a very simple POC so you can get familiar with these new concepts. We did that with a small portion of our code before even attempting to solve the PDF problem to better understand the full ecosystem (queuing, lambda, notification…). We now have 5 lambdas doing specific jobs (outside the PDF needs).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You can read more on this&lt;/strong&gt; from a more technical point of view: &lt;a href=&quot;https://hugo.alliau.me/2020/01/02/generate-pdfs-on-amazon-aws-with-php-and-puppeteer/&quot;&gt;Generate PDFs on Amazon AWS with PHP and Puppeteer&lt;/a&gt;, written by Hugo Alliaume, who helped a lot on this subject. It contains a lot of details, such as how we shipped chrome into the lambda.&lt;/p&gt;
</content:encoded><dc:creator>Tristan Bessoussa</dc:creator><dc:language>en</dc:language><category>faas</category><category>serverless</category><category>architecture</category><category>bref.sh</category><category>Symfony</category><category>sqs</category><category>lambda</category><category>php</category></item><item><title>How not to be depressed when working with WordPress</title><link>https://devops-life.com/blog/2019/04/10/state-of-wordpress-in-2019/</link><guid isPermaLink="true">https://devops-life.com/blog/2019/04/10/state-of-wordpress-in-2019/</guid><description>Ok, so you chose to start a new WordPress project (or someone chose for you)? Well good news, it&apos;s 2019, it doesn&apos;t have to suck from the start anymore;</description><pubDate>Wed, 10 Apr 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Ok, so you chose to start a new WordPress project (or someone chose for you)? Well good news, it’s 2019, it doesn’t have to suck from the start anymore;&lt;/p&gt;
&lt;p&gt;In this article, I’m not going to explain everything in details, that would require me to write a book about it. I will just point out directions: &lt;strong&gt;what you can do better than the broken official way of doing&lt;/strong&gt; that was legitimate 10 years ago. It’s designed for everyone, web developers, backend developers, front-end developers…&lt;/p&gt;
&lt;p&gt;I promise you that there is &lt;em&gt;no more than 3 minutes of reading&lt;/em&gt; per chapter.&lt;/p&gt;
&lt;p&gt;In the company I work for, we have 150+ WordPress sites. I made the decision for the new ones to adopt everything I wrote in this article, and slowly migrate the legacy ones. Already ~10% of them are using this architecture at the time I was writing this article.&lt;/p&gt;
&lt;p&gt;Note: I’m accepting &lt;a href=&quot;https://github.com/tristanbes/devops-life/blob/gh-pages/_posts/2019-04-10-state-of-wordpress-in-2019.md&quot;&gt;pull requests&lt;/a&gt; to fix english mistakes if you catch them.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;use-bedrock-edition&quot;&gt;Use Bedrock Edition&lt;/h2&gt;
&lt;p&gt;It’s &lt;strong&gt;THE&lt;/strong&gt; starting point of this article. Bedrock is a WordPress boilerplate created by &lt;a href=&quot;https://roots.io/&quot;&gt;Roots&lt;/a&gt;. It leverages the use of modern development tools and embrace the &lt;a href=&quot;https://12factor.net/&quot;&gt;12 Factor app methodology&lt;/a&gt;. Some of the things WordPress can’t/won’t do because of their dinosaur 🦖 status in the PHP world.&lt;/p&gt;
&lt;p&gt;The main advantages of this boilerplate are:&lt;/p&gt;
&lt;h4 id=&quot;support-of-environment-variables&quot;&gt;Support of environment variables&lt;/h4&gt;
&lt;p&gt;If you worked with modern software applications, you’re familiar with the concept of having your application configuration stored as environment variables.&lt;/p&gt;
&lt;p&gt;The benefit of having environment variables is for you to have multiple environments such as &lt;code&gt;production&lt;/code&gt;, &lt;code&gt;staging&lt;/code&gt; and &lt;code&gt;development&lt;/code&gt; where you can for example only enable sending emails on &lt;code&gt;production&lt;/code&gt;, without the need of constantly changing values depending on which environment you are.&lt;/p&gt;
&lt;p&gt;In 2019, every major frameworks has a concept of environnement variables in the PHP world: &lt;em&gt;Symfony&lt;/em&gt;, &lt;em&gt;Laravel&lt;/em&gt;, &lt;em&gt;Yii&lt;/em&gt;. It’s a common practice that exists since decades on Windows and Unix world.&lt;/p&gt;
&lt;p&gt;Environment variables can be stored as a file, for &lt;code&gt;development&lt;/code&gt; and injected in your web server for &lt;code&gt;staging&lt;/code&gt; and &lt;code&gt;production&lt;/code&gt; environments. Don’t freak out, a lot of decent hosting companies provide an UI to manipulate environment variables. For example, look at what &lt;a href=&quot;https://devcenter.heroku.com/articles/config-vars#using-the-heroku-dashboard&quot;&gt;Heroku does for environment variables&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Environment variables look a lot like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes devops-life-light devops-life-dark&quot; style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC;--shiki-light-bg:#FFFFFF;--shiki-dark-bg:#151C26; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC&quot;&gt;DATABASE_URL&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;mysql://john_doe:p4sSWorD@127.0.0.1:3306/db_awesome_blog&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC&quot;&gt;WP_ENV&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;development&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC&quot;&gt;WP_HOME&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;http://my-awesome-wordpress.vm&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC&quot;&gt;WP_SITEURL&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;=${&lt;/span&gt;&lt;span style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC&quot;&gt;WP_HOME&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;/wp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;better-directory-structure&quot;&gt;Better directory structure&lt;/h4&gt;
&lt;p&gt;Everything is organized better and as a result, it’s easier to find what you were looking for. The structure look likes:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes devops-life-light devops-life-dark&quot; style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC;--shiki-light-bg:#FFFFFF;--shiki-dark-bg:#151C26; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;├──&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; composer.json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;├──&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; .env&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;-----------------&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; where&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; majority&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; of&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; the&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; configuration&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; will&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; happen,&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; must&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; be&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; committed&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; inside&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; repository&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;├──&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; config&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;│&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;   ├──&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; application.php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;│&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;   └──&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; environments&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;│&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;       ├──&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; development.php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;│&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;       ├──&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; staging.php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;│&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;       └──&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; production.php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;├──&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; vendor&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;  &amp;lt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;--------------&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; where&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; the&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; 3rd&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; party&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; code&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; lives&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;└──&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; web&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;    ├──&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; app&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;    │&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;   ├──&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; mu-plugins&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;    │&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;   ├──&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; plugins&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;    │&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;   ├──&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; themes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;    │&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;   └──&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; uploads&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;    ├──&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; wp-config.php&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;--&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; do&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; not&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; touch&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; file&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;    ├──&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; index.php&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;------&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; entry&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; point&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; of&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; your&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; application,&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; each&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; web&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; request&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; are&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; send&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; through&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; file&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;    └──&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; wp&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;-------------&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; WordPress&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt; files&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bonus point to security since the web root (entry point of your application) &lt;code&gt;web/index.php&lt;/code&gt; is isolated from the rest of the structure. It means that your visitor can’t access your &lt;code&gt;config/production.php&lt;/code&gt; file since it’s outside &lt;code&gt;web/&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id=&quot;reproducible-builds&quot;&gt;Reproducible builds&lt;/h4&gt;
&lt;p&gt;The concept of &lt;a href=&quot;https://en.wikipedia.org/wiki/Reproducible_builds&quot;&gt;reproducible builds&lt;/a&gt; can sum up as: no matter when or where i’m going to build and deploy the application, I will always have the same version of the code (and plugins).&lt;/p&gt;
&lt;p&gt;Since you’re going to be working on you local environment (if it’s not already the case 👀), you need to guarantee that what you have on your computer will be exactly the same that what you’ll have on your production. Let me take an example on why this is important.&lt;/p&gt;
&lt;p&gt;Let’s say you’ve been running a website in production for like a year until there’s a nasty hardware crash on you server. Now all your files are lost. Luckily for you you did things right and had your &lt;code&gt;uploads/&lt;/code&gt; directory and database backup somewhere in the cloud.&lt;/p&gt;
&lt;p&gt;The next step will be to re-install WordPress using the database and media backup. Then re-install the 20 plugins you had on that website. In one year, plugins evolve (and it’s good), but your theme, or some of your features might not be compatible with WooCommerce &lt;code&gt;v3.5&lt;/code&gt;. It might result of a &lt;strong&gt;broken website&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Another example is when you’re working as a team on a project, without reproducible builds you will end up with the frontend person working with WordPress 5.1.1 while the developer still has version 4.9.10 and won’t be able to reproduce the bug you have with that setup.&lt;/p&gt;
&lt;p&gt;Because of this concept, you’ll need to &lt;strong&gt;forbid any type of plugins/themes installation or updating from the WordPress backend&lt;/strong&gt;. By default Bedrock will take care of this when you are in the &lt;code&gt;production&lt;/code&gt; environment. Since WordPress is also a composer dependency, updating it is as simple as &lt;code&gt;composer update roots/wordpress&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But how to guarantee reproducible builds? Well the answer is just below: use &lt;strong&gt;Composer&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;use-composer&quot;&gt;Use Composer&lt;/h2&gt;
&lt;p&gt;Bedrock boilerplate allows you to manage dependencies of your project using &lt;a href=&quot;https://getcomposer.org/&quot;&gt;Composer&lt;/a&gt; like &lt;strong&gt;any&lt;/strong&gt; modern and decent PHP project should do (WordPress, I’m looking at you right now).&lt;/p&gt;
&lt;p&gt;Do you want to install &lt;code&gt;WooCommerce&lt;/code&gt;? Go inside your terminal and type:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;composer require woocommerce/woocommerce&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;It’ll fetch the latest version and freeze the version you got on your &lt;code&gt;composer.lock&lt;/code&gt;. This file is what is used to always get the same version of everything no matter where you download your project dependencies, or where you do it when you run &lt;code&gt;composer install&lt;/code&gt;. Because of the importance of this file, it has to be committed ⚠️.&lt;/p&gt;
&lt;p&gt;A good article can be found on &lt;a href=&quot;https://roots.io/using-composer-with-wordpress/&quot;&gt;how to use composer with WordPress&lt;/a&gt; on roots.io website.&lt;/p&gt;
&lt;p&gt;All the plugins are also installable via composer; How? &lt;strong&gt;3 scenarios are possible&lt;/strong&gt;:&lt;/p&gt;
&lt;h4 id=&quot;the-plugin-provides-a-composerjson&quot;&gt;The plugin provides a &lt;code&gt;composer.json&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;The developer or company behind the plugin is not locked in a WordPress thing of the past syndrome and has developed modern applications. This would mean that their plugin also ships a &lt;a href=&quot;https://github.com/woocommerce/woocommerce/blob/master/composer.json&quot;&gt;composer.json&lt;/a&gt; file that allows installation through composer. With this scenario you can search for your plugin inside &lt;a href=&quot;https://packagist.org/?query=woocommerce&quot;&gt;packagist.org&lt;/a&gt; which is a repository containing all the public PHP packages in the universe ✨.&lt;/p&gt;
&lt;h4 id=&quot;the-plugin-does-not-provide-a-composerjson&quot;&gt;The plugin does not provide a &lt;code&gt;composer.json&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;If you don’t see the plugin on &lt;a href=&quot;https://packagist.org/&quot;&gt;packagist.org&lt;/a&gt;, then the plugin does not have a &lt;code&gt;composer.json&lt;/code&gt; file.
Don’t worry, &lt;strong&gt;wpackagist&lt;/strong&gt; comes to the rescue. Outlandish created this project to offer a way to install 100% of the plugins and themes using the main WordPress repository. So, this particular WordPress plugin cannot be found on packagist.org, so let’s &lt;a href=&quot;https://wpackagist.org/&quot;&gt;use wpackagist&lt;/a&gt; instead:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;composer require wpackagist-plugin/cookie-law-info&lt;/code&gt;&lt;/p&gt;
&lt;h4 id=&quot;the-plugin-is-a-paidprivate-one-and-does-not-have-a-composerjson&quot;&gt;The plugin is a paid/private one and does not have a &lt;code&gt;composer.json&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;You’ll have multiple options from there:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/elementor/elementor/issues/4042&quot;&gt;Open a ticket to their support&lt;/a&gt; or on their Github and ask to provide a way to install their plugin via composer. You are their customer, you are paying for a product that you can’t install in a proper way. They should care.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;👎 Bad players are ACF Pro, Elementor Pro, Formidable Forms&lt;/li&gt;
&lt;li&gt;👍 Good player is &lt;a href=&quot;https://deliciousbrains.com/wp-migrate-db-pro/doc/installing-via-composer/&quot;&gt;Deliciours brains&lt;/a&gt; (Migrate DB Pro, Offload Media…).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/PhilippBaschke/acf-pro-installer&quot;&gt;Use alternative solutions&lt;/a&gt; when available.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a new private Github repository (it’s free), copy/paste the code there and add a &lt;a href=&quot;https://gist.github.com/tristanbes/fbacfb2ce6990e7fdc7411a73715fd92&quot;&gt;composer.json like in this example&lt;/a&gt;, tag a new release using &lt;a href=&quot;https://help.github.com/en/articles/creating-releases&quot;&gt;Github releases&lt;/a&gt;, and then require this package using a custom repository. All the procedure is &lt;a href=&quot;https://roots.io/guides/private-or-commercial-wordpress-plugins-as-composer-dependencies/&quot;&gt;listed in detail on roots.io website&lt;/a&gt;. I’d advise, if you have the budget for, to use &lt;a href=&quot;https://packagist.com/&quot;&gt;Private Packagist&lt;/a&gt; to host your private packages.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;use-a-better-templating-system-than-php-im-yelling-timber&quot;&gt;Use a better templating system than PHP: “I’m yelling timber”!&lt;/h2&gt;
&lt;p&gt;What about the templating system?
PHP is not a good templating system. Hard to read, hard to write, and let’s not fool ourselves, we’re no longer in the early 2010, we have tools created especially for each use case. The best PHP templating system is &lt;a href=&quot;https://twig.symfony.com/&quot;&gt;Twig&lt;/a&gt;. It is widely used in the Symfony ecosystem (but not only).&lt;/p&gt;
&lt;p&gt;So how can we use Twig inside a WordPress project? Luckily for us, there’s a project called  &lt;a href=&quot;https://github.com/timber/timber&quot;&gt;Timber&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Plugin to write WordPress themes with object-oriented code and the Twig Template Engine&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It adds a View / Controller model that will have a huge benefit on the readability and the maintenance of your theme.&lt;/p&gt;
&lt;p&gt;Once you start writing your themes using Twig, &lt;strong&gt;you won’t go back to plain PHP&lt;/strong&gt;. Period.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pro tip&lt;/strong&gt;: Since you’re not designing your themes with plain PHP anymore but with Twig, the server needs to transform your &lt;code&gt;.twig&lt;/code&gt; files into &lt;code&gt;.php&lt;/code&gt; files. This phase is called compilation. It adds extra time to do that. Luckily there’s a cache system that stores the compiled templates on your file system so Twig doesn’t recompile your twig templates on each request that your server needs to process.&lt;/p&gt;
&lt;p&gt;By default, Timber does not activate that cache.&lt;/p&gt;
&lt;p&gt;Here’s how to activate it safely. You’ll save up to 40% of generation time by doing so. Inside your theme’s &lt;code&gt;functions.php&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes devops-life-light devops-life-dark&quot; style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC;--shiki-light-bg:#FFFFFF;--shiki-dark-bg:#151C26; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#5F6B7D;--shiki-light-font-style:italic;--shiki-dark:#8795A9;--shiki-dark-font-style:italic&quot;&gt;// Activate Twig caching&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;class_exists&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;Timber&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; &amp;amp;&amp;amp;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#AE3F14;--shiki-dark:#F3996F&quot;&gt; WP_ENV&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; ===&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;production&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;    Timber&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC&quot;&gt;$cache &lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#AE3F14;--shiki-dark:#F3996F&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#5F6B7D;--shiki-light-font-style:italic;--shiki-dark:#8795A9;--shiki-dark-font-style:italic&quot;&gt;// Replace WP_ENV === &apos;production&apos; by!WP_DEBUG if you are NOT in a Bedrock structure&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;figure class=&quot;image&quot;&gt;&lt;a href=&quot;https://devops-life.com/img/timber-cache.png&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;img src=&quot;https://devops-life.com/img/timber-cache.png&quot; alt=&quot;Difference of rendering time before/after enabling Twig cache system. More details on this Blackfire trace .&quot; loading=&quot;lazy&quot; width=&quot;688&quot; height=&quot;37&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;figcaption&gt;Difference of rendering time before/after enabling Twig cache system. &lt;a href=&apos;https://blackfire.io/profiles/compare/5b79d23e-3bb8-4089-8e56-82aaae679b6b/graph&apos;&gt;More details on this Blackfire trace&lt;/a&gt;.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Note that it caches the the compiled php template and not the data. Any edits made to the &lt;code&gt;.twig&lt;/code&gt; templates on the production won’t be reflected unless you clear the cache. (Who edits files on production though? 😱 Certainly not you!)&lt;/p&gt;
&lt;h2 id=&quot;prefer-storing-your-assets-on-the-cloud&quot;&gt;Prefer storing your assets on the cloud&lt;/h2&gt;
&lt;p&gt;Storing your assets on the local filesystem under &lt;code&gt;uploads/&lt;/code&gt; directory is nice, but at some point, you’ll have to synchronize this folder through all your environments, otherwise your local version will a complete different set of medias than the production and vice-versa.&lt;/p&gt;
&lt;p&gt;Remember when we talked about reproducable builds? Wouldn’t it be nice if we can store our media remotely so we don’t use local filesystem? Spoiler alert, yes, it’s nice.&lt;/p&gt;
&lt;p&gt;The one other major advantage of this technique is &lt;strong&gt;allow your website to scale&lt;/strong&gt; to receive a huge spike of traffic load. All you have to do is to add more servers (horizontal scaling), since you have a reproducible build, the project and plugins will be the same on server number 1, 2, … 23, 24.&lt;/p&gt;
&lt;p&gt;Services you can use to store your media on the cloud :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/s3/&quot;&gt;Amazon S3&lt;/a&gt;. The most famous, yet, not the cheapest one 💸.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.digitalocean.com/products/spaces/&quot;&gt;Digital Ocean Spaces&lt;/a&gt;. S3-compatible, 45 locations available.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.scaleway.com/object-storage/&quot;&gt;Scaleway&lt;/a&gt;. Cheap storage, S3-compatible but only 2 locations available (Paris and Amsterdam).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;✅ Remember to always use a bucket location near where the majority of your audience live.
I would advise you to choose a “S3 compliant API” since there’s not much WordPress plugin that allow you to use a remote storage that work in majority of use-case.&lt;/p&gt;
&lt;p&gt;Here’s a list of free plugins that allow you to store your media on different cloud storage:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://wordpress.org/plugins/ilab-media-tools/&quot;&gt;MediaCloud&lt;/a&gt; (free)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/humanmade/S3-Uploads&quot;&gt;humanmade/s3-uploads&lt;/a&gt; (free)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My wish for WordPress is for them to use an filesystem abstraction such as &lt;a href=&quot;https://flysystem.thephpleague.com/docs/&quot;&gt;Flysystem&lt;/a&gt;. This way, we no longer have to rely on plugins to hack into WordPress to tell it to upload medias and assets elsewhere. Until this happens, you can use one of the plugins above.&lt;/p&gt;
&lt;h2 id=&quot;know-tools-youre-working-with-acf-can-hurt-your-website-performance&quot;&gt;Know tools you’re working with: ACF can hurt your website performance&lt;/h2&gt;
&lt;p&gt;If you’re a user of Advanced Custom Field, known as ACF, then you must know some of the performance bottleneck you might have if you’re abusing the &lt;code&gt;get_field&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;You must only expose the variables you need per page. I’ve seen a lot of example, even in the documentation, where you are invited to do:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes devops-life-light devops-life-dark&quot; style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC;--shiki-light-bg:#FFFFFF;--shiki-dark-bg:#151C26; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC&quot;&gt;$context&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;options&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt; get_fields&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;options&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;);&lt;/span&gt;&lt;span style=&quot;--shiki-light:#5F6B7D;--shiki-light-font-style:italic;--shiki-dark:#8795A9;--shiki-dark-font-style:italic&quot;&gt; // retrieve all options fields from the database&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;Timber&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;render&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;index.twig&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC&quot;&gt; $context&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;);&lt;/span&gt;&lt;span style=&quot;--shiki-light:#5F6B7D;--shiki-light-font-style:italic;--shiki-dark:#8795A9;--shiki-dark-font-style:italic&quot;&gt; // render the index.twig page with the options passed to the twig context so you can use them inside your view.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you do that, it will trigger one database call per field you’re getting. So let’s say you have 35 fields under the option category, it’ll trigger 35 database call just to get those fields, but, do you really need those 35 fields on all pages?&lt;/p&gt;
&lt;p&gt;What to do instead?&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes devops-life-light devops-life-dark&quot; style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC;--shiki-light-bg:#FFFFFF;--shiki-dark-bg:#151C26; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC&quot;&gt;$context&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;options&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;twitter_link&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt; get_fields&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;twitter_link&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;options&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC&quot;&gt;$context&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;options&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;logo_footer_img&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt; get_fields&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;logo_img&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;options&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;Timber&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6139C4;--shiki-dark:#B8A8FA&quot;&gt;render&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#0D7358;--shiki-dark:#5FCDA8&quot;&gt;index.twig&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC&quot;&gt; $context&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code astro-code-themes devops-life-light devops-life-dark&quot; style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC;--shiki-light-bg:#FFFFFF;--shiki-dark-bg:#151C26; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;twig&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#5F6B7D;--shiki-light-font-style:italic;--shiki-dark:#8795A9;--shiki-dark-font-style:italic&quot;&gt;{# use them inside your .twig template; #}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt;footer&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-light:#AE3F14;--shiki-dark:#F3996F&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&apos;{{&lt;/span&gt;&lt;span style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC&quot;&gt; options&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt;twitter_link&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; }}&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC&quot;&gt;Follow us&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;    &amp;lt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt;img&lt;/span&gt;&lt;span style=&quot;--shiki-light:#AE3F14;--shiki-dark:#F3996F&quot;&gt; src&lt;/span&gt;&lt;span style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&apos;{{&lt;/span&gt;&lt;span style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC&quot;&gt; options&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt;logo_footer_img&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; }}&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#2855BC;--shiki-dark:#8DAEF6&quot;&gt;footer&lt;/span&gt;&lt;span style=&quot;--shiki-light:#4B586B;--shiki-dark:#A2AEBE&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In real world project, doing this kind of optimization leads to important performance improvements.&lt;/p&gt;
&lt;figure class=&quot;image&quot;&gt;&lt;a href=&quot;https://devops-life.com/img/blackfire-acf.png&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;img src=&quot;https://devops-life.com/img/blackfire-acf.png&quot; alt=&quot;Difference of rendering time before/after retrieving only the strict necessary ACF fields. -850 requests to the database and -37% of rendering time! More details on this Blackfire trace .&quot; loading=&quot;lazy&quot; width=&quot;688&quot; height=&quot;38&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;figcaption&gt;Difference of rendering time before/after retrieving only the strict necessary ACF fields. -850 requests to the database and -37% of rendering time! &lt;a href=&apos;https://blackfire.io/profiles/compare/3984573f-3fc2-48fc-9683-2668c3a4a7e5/graph&apos;&gt;More details on this Blackfire trace&lt;/a&gt;.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Explicit is better than implicit. It helps your colleague to provide a more accurate code review of your work. That work is harder when you don’t know what your template has access to.&lt;/p&gt;
&lt;p&gt;And what about videos and images fields? If you are using them, you should know that if you retrieve them “as is” using the &lt;code&gt;get_field(&apos;my_youtube_video&apos;)&lt;/code&gt;, the code will try to fetch the metadata (size, orientation, length…) of the videos and images.&lt;/p&gt;
&lt;p&gt;What’s the matter here? Well, since you’re hosting your medias on the cloud (or a remote service, eg: YouTube in this example), &lt;strong&gt;the video and images has to be downloaded on the server side&lt;/strong&gt; to allow retrieve this information you won’t need in 98% of the cases. In this situation doing external HTTP calls from your server to the internet adds precious extra time and kills your site’s performance.&lt;/p&gt;
&lt;p&gt;I’ve seen project spending almost 1 second doing external calls, preventing the server to send HTML to the browser by fetching just 10 images information from the cloud.&lt;/p&gt;
&lt;p&gt;In those cases you need to use &lt;code&gt;get_field(&apos;my_youtube_video&apos;, null, false)&lt;/code&gt;. The last option (&lt;code&gt;false&lt;/code&gt;), tells to retrieve the field (in this case, the YouTube url of the video, without any formatting or additionnal treatment). One other case is to set the configuration of the field as a “URL” field, (and not oEmbed), and for images, “Image URL”&lt;/p&gt;
&lt;figure class=&quot;image&quot;&gt;&lt;a href=&quot;https://devops-life.com/img/external_http_calls_blackfire.png&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;img src=&quot;https://devops-life.com/img/external_http_calls_blackfire.png&quot; alt=&quot;Difference of rendering time before/after configuring fields as URL fields instead of oEmbed. All the external HTTP calls the server made to fetch the metadata of images or videos were eliminated (-7) and it saved 35% of time (-846ms)! More details on this Blackfire trace .&quot; loading=&quot;lazy&quot; width=&quot;688&quot; height=&quot;39&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;figcaption&gt;Difference of rendering time before/after configuring fields as URL fields instead of oEmbed. All the external HTTP calls the server made to fetch the metadata of images or videos were eliminated (-7) and it saved 35% of time (-846ms)! &lt;a href=&apos;https://blackfire.io/profiles/compare/29fc43d1-fce1-447e-9d4e-cef4b2f53750/graph&apos;&gt;More details on this Blackfire trace&lt;/a&gt;.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id=&quot;lint-your-code&quot;&gt;Lint your code&lt;/h2&gt;
&lt;p&gt;Is your theme shipping CSS, Javascript and some PHP? You should lint them, make sure you write code compliant with industry standard. Use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://eslint.org/&quot;&gt;ESlint&lt;/a&gt; - Javascript&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stylelint.io/&quot;&gt;stylelint&lt;/a&gt; - CSS&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://prettier.io/&quot;&gt;Prettier&lt;/a&gt; - Javascript, CSS…&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cs.symfony.com/&quot;&gt;PHP-CS-Fixer&lt;/a&gt; - PHP (Example of our &lt;a href=&quot;https://gist.github.com/tristanbes/8f29b6f9336a77fd9b205f3453aae892&quot;&gt;.php_cs_dist file&lt;/a&gt; that contains linting rules)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Use whatever building tool/task runner you feel comfortable with (&lt;a href=&quot;https://gulpjs.com/&quot;&gt;Gulp&lt;/a&gt;, &lt;a href=&quot;https://brunch.io&quot;&gt;Brunch&lt;/a&gt;). If you don’t have picked up a tool yet, and you need a simple one, I’d suggest you take a look at &lt;strong&gt;&lt;a href=&quot;https://github.com/Yproximite/yProx-cli&quot;&gt;yProx-CLI&lt;/a&gt;&lt;/strong&gt; that we’ve been using at Yproximite for some times and just released as an open source project.&lt;/p&gt;
&lt;p&gt;For your own sanity, please don’t open code you’re downloading from the plugins you use. You might be shocked by the poor code quality you’re dealing with and might end up re-writing a whole lot of WordPress plugins.&lt;/p&gt;
&lt;h2 id=&quot;use-a-continuous-integration-ci-service&quot;&gt;Use a continuous integration (CI) service&lt;/h2&gt;
&lt;p&gt;Use a CI (&lt;a href=&quot;https://travis-ci.org/&quot;&gt;Travis&lt;/a&gt;, &lt;a href=&quot;https://about.gitlab.com/product/continuous-integration/&quot;&gt;Gitlab&lt;/a&gt;, &lt;a href=&quot;https://circleci.com/&quot;&gt;CircleCI&lt;/a&gt;, &lt;a href=&quot;https://jenkins.io/&quot;&gt;Jenkins&lt;/a&gt;).
Here’s our base &lt;code&gt;.travis.yml&lt;/code&gt; file that ensure our &lt;code&gt;composer.json&lt;/code&gt; is valid, that our PHP Code is linted and same goes for our JavaScript files; No pull requests should be merged if you have linting errors that will never be fixed once they hit the production. This will allow your peer to review your code and focus on important things.&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes devops-life-light devops-life-dark&quot; style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC;--shiki-light-bg:#FFFFFF;--shiki-dark-bg:#151C26; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;dist: trusty&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;language: php&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;php:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  - &apos;7.3&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;cache:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  yarn: true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  directories:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    - ./vendor&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    - ./node_modules&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    - ~/.composer/cache/files&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;before_install:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  - |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    if [ -f &quot;package.json&quot; ]; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        set -e # properly fails Travis if one of the following commands fails&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        nvm install v10&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        nvm use 10&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        node --version&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        npm --version&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        npm install --global yarn&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        set +e # re-enable previous behaviour&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;script:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  - composer validate&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  - composer install&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  - vendor/bin/php-cs-fixer fix -v --dry-run --using-cache=no&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  - if [ -f &quot;package.json&quot; ]; then yarn install; fi;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  - if [ -f &quot;package.json&quot; ]; then yarn lint; fi;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  - if [ -f &quot;package.json&quot; ]; then yarn build; fi;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;setup-an-automated-dependency-updates-tool&quot;&gt;Setup an automated dependency updates tool&lt;/h2&gt;
&lt;p&gt;Since we have 150 WordPress projects in our organization, running &lt;code&gt;composer update&lt;/code&gt; on all of them once per week is not something we want to do, we had to figure how to handle the updates.&lt;/p&gt;
&lt;p&gt;We were using &lt;a href=&quot;https://managewp.com/&quot;&gt;ManageWP&lt;/a&gt; to update WordPress and all of their dependencies, but this option is now off the table since it breaks the concept of reproducible builds. Nothing should alter/mutate your environment. The code should be exactly the same like in your git repository.&lt;/p&gt;
&lt;p&gt;One viable option for us was to delegate the updating process to &lt;a href=&quot;https://dependabot.com/&quot;&gt;Dependabot&lt;/a&gt; which job is to open a pull request when updates are available. Alternatives exists, for example, &lt;a href=&quot;https://renovatebot.com/&quot;&gt;Renovate&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We configured it to automatically merge the pull requests if the package update is a major or a minor (&lt;code&gt;v1.0.0 -&amp;gt; v1.1.4&lt;/code&gt; will be merged automatically, but &lt;code&gt;v1.0.0 -&amp;gt; v2.0.0&lt;/code&gt; won’t);&lt;/p&gt;
&lt;p&gt;⚠️ The WordPress ecosystem does not necessarily respect the &lt;a href=&quot;https://semver.org/&quot;&gt;SemVer&lt;/a&gt;, because… (I’m still looking for a reason…).&lt;/p&gt;
&lt;p&gt;Since we choose a “Platform as a Service” (PaaS) to host all of our WordPress, as soon as a pull request gets merged on master branch, the project gets re-deployed on the production.&lt;/p&gt;
&lt;p&gt;No matter which hosting you chose, you should have automatic deployment, otherwise, the project will be up-to-date but will never hit the production if you need to re-deploy each project if you have a huge amount of them.&lt;/p&gt;
&lt;h2 id=&quot;choose-the-right-hosting&quot;&gt;Choose the right hosting&lt;/h2&gt;
&lt;p&gt;I would strongly advise to go for a PaaS. The main advantages of a Platform as a Service (PaaS) are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No maintenance needed for the server or the software&lt;/li&gt;
&lt;li&gt;Automated deployments&lt;/li&gt;
&lt;li&gt;Vertical, horizontal scaling ready&lt;/li&gt;
&lt;li&gt;Cheap&lt;/li&gt;
&lt;li&gt;Easy&lt;/li&gt;
&lt;li&gt;Isolated (no cross contamination from one site A to site B when hosted on the same server)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some examples of PaaS are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.heroku.com/&quot;&gt;🌍 Heroku&lt;/a&gt; (from 14$/month)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://scalingo.com/&quot;&gt;🇫🇷 Scalingo&lt;/a&gt; (from 10,80€/month)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.clever-cloud.com/en/&quot;&gt;🇫🇷 Clever Cloud&lt;/a&gt; (from 26,50€/month)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;⛔️ Don’t ever use a FTP or a SFTP to deploy to the production. Since you need to build your project by running &lt;code&gt;composer install&lt;/code&gt; and maybe build assets by running &lt;code&gt;yarn build&lt;/code&gt;, the long dead process of copy/paste the project on your FTP are &lt;strong&gt;definitely and finally&lt;/strong&gt; dead ⛔️&lt;/p&gt;
&lt;h2 id=&quot;what-you-should-already-be-doing&quot;&gt;What you should already be doing&lt;/h2&gt;
&lt;p&gt;Here’s a list of things you should already be doing, so they don’t deserve a chapter on their own:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Work with a Virtual Machine, that will allow everyone to have the same environment. You can use &lt;a href=&quot;https://roots.io/trellis/&quot;&gt;trellis&lt;/a&gt; or &lt;a href=&quot;http://www.manala.io/&quot;&gt;manala&lt;/a&gt; if you’re more technical&lt;/li&gt;
&lt;li&gt;Use the latest version of PHP available&lt;/li&gt;
&lt;li&gt;Work with pull request&lt;/li&gt;
&lt;li&gt;Review, and ask reviews on your code by your peers&lt;/li&gt;
&lt;li&gt;Configure Github to disallow pull request merging if your pull request doesn’t have reviews.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;note-for-wordpress-plugins-developers&quot;&gt;Note for WordPress plugins developers&lt;/h2&gt;
&lt;p&gt;In order to make the WordPress ecosystem a better place please, you have to respect at least 5 things:&lt;/p&gt;
&lt;p&gt;⚠️ Don’t assume that your plugin user will stores their uploaded files on the local file system. &lt;strong&gt;Assume they don’t&lt;/strong&gt;. Abstract your file manipulation by using for instance &lt;a href=&quot;https://flysystem.thephpleague.com/docs/&quot;&gt;Flysystem&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The main reason behind this reasoning is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When hosting your application on the cloud, you might want to be able to scale your application by adding new servers.&lt;/li&gt;
&lt;li&gt;When you host your WordPress on an &lt;a href=&quot;https://www.digitalocean.com/community/tutorials/what-is-immutable-infrastructure&quot;&gt;immutable infrastructure&lt;/a&gt;, which basically means
&lt;ul&gt;
&lt;li&gt;Either you’re not able to write to the server, preventing any modifications&lt;/li&gt;
&lt;li&gt;Either the modifications your made are lost when the project gets redeployed. (A deploy happens each time you merge code on your code repository or when you scale (add or remove servers) on your application. Only what is on your git repository gets deployed on the server.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;⚙ Provide a way to configure your plugin through environment variables, and use a fallback using the database if the environment variable is not found.
&lt;strong&gt;Environment variables &amp;gt; Configuration stored inside the DB&lt;/strong&gt; in terms of importance; This allow us to automate plugins installation and put all the configuration in one place.&lt;/p&gt;
&lt;p&gt;🎻 Make your plugin installable with &lt;strong&gt;composer&lt;/strong&gt;, it requires a &lt;code&gt;composer.json&lt;/code&gt; file with 10 lines of code at the minimum. Just watch some examples &lt;a href=&quot;https://github.com/deliciousbrains/wp-amazon-s3-and-cloudfront/blob/master/composer.json&quot;&gt;here&lt;/a&gt;, &lt;a href=&quot;https://github.com/awesomemotive/WP-Mail-SMTP/blob/master/composer.json#L4&quot;&gt;here&lt;/a&gt; or &lt;a href=&quot;https://github.com/Yoast/wordpress-seo/blob/trunk/composer.json&quot;&gt;here&lt;/a&gt;. The next step is to tag your releases using &lt;a href=&quot;https://github.com/deliciousbrains/wp-migrate-db/releases&quot;&gt;Github Releases&lt;/a&gt; respecting SemVer and submit your package to &lt;a href=&quot;https://packagist.org/packages/submit&quot;&gt;packagist.org&lt;/a&gt; by entering the Github URL of your repository.&lt;/p&gt;
&lt;p&gt;🔒 Support &lt;a href=&quot;https://www.php.net/supported-versions.php&quot;&gt;maintained version of PHP&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;💅🏽 Lint your PHP code using PHP-CS-Fixer FFS! Choose between PSR2 or Symfony preset, but use one and fix your code.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;If you want to look at a project structure implementing the majority of the point made in this article, you can &lt;a href=&quot;https://github.com/tristanbes/wordpress-boilerplate&quot;&gt;take a look at my WordPress boilerplate example based on Bedrock and Timber&lt;/a&gt;.
This repository should not be used “as is”, but it’ll help you understand how to structure your project files.&lt;/p&gt;
</content:encoded><dc:creator>Tristan Bessoussa</dc:creator><dc:language>en</dc:language><category>WordPress</category><category>Architecture</category><category>composer</category><category>best practices</category><category>Bedrock</category></item><item><title>PaaS benchmark: Clever Cloud vs Scalingo vs Hidora</title><link>https://devops-life.com/en/blog/2019/01/24/clever-cloud-vs-scalingo/</link><guid isPermaLink="true">https://devops-life.com/en/blog/2019/01/24/clever-cloud-vs-scalingo/</guid><description>This series of posts shares hands-on feedback on French PaaS providers, with one problem in mind: hosting a fleet of 120+ WordPress sites.</description><pubDate>Thu, 24 Jan 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This series of posts shares hands-on feedback on French PaaS providers, with one problem in mind: hosting a fleet of 120+ WordPress sites.&lt;/p&gt;
&lt;p&gt;In this series:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Part 1. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/19/french-paas-for-wordpress-comparison/&quot;&gt;Background to the French PaaS comparison&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 2. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/20/ovh-cloud-web-paas-review/&quot;&gt;PaaS comparison: OVH falls overboard&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 3. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/21/clever-cloud-paas-review/&quot;&gt;PaaS comparison: Clever Cloud, &lt;del&gt;the boat is taking on water&lt;/del&gt; the captain rights the ship&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 4. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/22/scalingo-paas-review/&quot;&gt;PaaS comparison: Scalingo stays afloat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 5. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/23/platform-sh-paas-review/&quot;&gt;PaaS comparison: Platform.sh lies at anchor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 6. &lt;a href=&quot;https://devops-life.com/en/blog/2021/01/19/swiss-paas-hidora-review/&quot;&gt;PaaS comparison: Hidora, the Swiss cloud&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 7. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/24/clever-cloud-vs-scalingo/&quot;&gt;PaaS comparison: performance, Clever Cloud VS Scalingo VS Hidora&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;EDIT 01/21&lt;/strong&gt; The performance tests have been rerun. The first batch, from 2019, is available here: &lt;strong&gt;&lt;a href=&quot;https://docs.google.com/spreadsheets/d/1hrny3Rf4RB7qqezy8jHQ_DbZXGzBK293sttXRlmxUMA/edit?usp=sharing&quot;&gt;all ~80 tests from the 2019 PaaS performance comparison between Scalingo and Clever Cloud&lt;/a&gt;&lt;/strong&gt;; it contained 80 tests.
In 2021, I kept the tests much more concise; they’re available in &lt;a href=&quot;https://docs.google.com/spreadsheets/d/1Lv9eyBcLTh2hEt7-9YxrTPGz5QUB1OQ2GbOVl-xmahA/edit?usp=sharing&quot;&gt;this spreadsheet&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;measured-performance&quot;&gt;Measured performance&lt;/h2&gt;
&lt;p&gt;Included in this benchmark:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;✅ Clever Cloud&lt;/li&gt;
&lt;li&gt;✅ Scalingo&lt;/li&gt;
&lt;li&gt;✅ Hidora&lt;/li&gt;
&lt;li&gt;⛔️ OVH Cloud Web (couldn’t get the project running, see Part II)&lt;/li&gt;
&lt;li&gt;⛔️ Platform.sh (too expensive for our WordPress use case)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The tests were run under real-world conditions. I took a “representative” WordPress site that’s already in production with us. These aren’t tests on a plugin-free WordPress with a single blank page.&lt;/p&gt;
&lt;p&gt;I ran the tests with a load testing tool (&lt;a href=&quot;https://k6.io/&quot;&gt;k6.io&lt;/a&gt;) from a server in Paris, FR: 5 unique visitors per second for 2 minutes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The tests were run under the same conditions on every PaaS&lt;/strong&gt;: same versions of WordPress and the plugins, same page tested. No optimizations, except for tuning the &lt;code&gt;pm.max_children&lt;/code&gt; variable to handle request concurrency in &lt;code&gt;php-fpm&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Before recording the results, I spent some time finding the optimal value for the &lt;code&gt;pm.max_children&lt;/code&gt; setting, which tells each scaler how many concurrent PHP requests it can handle.&lt;/p&gt;
&lt;p&gt;Set it too high, and performance tanks because there isn’t enough RAM (it swaps). Set it too low, and the scaler is throttled, and so is your performance.&lt;/p&gt;
&lt;p&gt;⚠️ Note that the number of unique visitors varies, even when the test is set to 5 unique visitors per second for 2 minutes. I never got 5x60x2 = 600 requests. So remember to look at the number of requests the server actually had to handle. ⚠️&lt;/p&gt;
&lt;h4 id=&quot;test-results-excerpts&quot;&gt;Test results (excerpts)&lt;/h4&gt;
&lt;h5 id=&quot;5-clients-per-second-over-2-minutes&quot;&gt;5 clients per second over 2 minutes&lt;/h5&gt;
&lt;div class=&quot;table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Table&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Price/month&lt;/th&gt;
&lt;th&gt;Details&lt;/th&gt;
&lt;th&gt;DB&lt;/th&gt;
&lt;th&gt;Average (ms)&lt;/th&gt;
&lt;th&gt;Number of requests&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Scalingo&lt;/td&gt;
&lt;td&gt;€10.8&lt;/td&gt;
&lt;td&gt;1x S&lt;/td&gt;
&lt;td&gt;256M&lt;/td&gt;
&lt;td&gt;492&lt;/td&gt;
&lt;td&gt;335&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scalingo&lt;/td&gt;
&lt;td&gt;€18.0&lt;/td&gt;
&lt;td&gt;1x M&lt;/td&gt;
&lt;td&gt;256M&lt;/td&gt;
&lt;td&gt;451&lt;/td&gt;
&lt;td&gt;342&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clever Cloud&lt;/td&gt;
&lt;td&gt;€11.9&lt;/td&gt;
&lt;td&gt;1x nano&lt;/td&gt;
&lt;td&gt;XXS-Med (512MB)&lt;/td&gt;
&lt;td&gt;884&lt;/td&gt;
&lt;td&gt;279&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clever Cloud&lt;/td&gt;
&lt;td&gt;€20.3&lt;/td&gt;
&lt;td&gt;1x XS&lt;/td&gt;
&lt;td&gt;XXS-Med (512MB)&lt;/td&gt;
&lt;td&gt;588&lt;/td&gt;
&lt;td&gt;313&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hidora&lt;/td&gt;
&lt;td&gt;€15.78&lt;/td&gt;
&lt;td&gt;2GB&lt;/td&gt;
&lt;td&gt;256MB&lt;/td&gt;
&lt;td&gt;2,042&lt;/td&gt;
&lt;td&gt;239&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hidora&lt;/td&gt;
&lt;td&gt;€28.70&lt;/td&gt;
&lt;td&gt;2GB&lt;/td&gt;
&lt;td&gt;512MB&lt;/td&gt;
&lt;td&gt;1,110&lt;/td&gt;
&lt;td&gt;237&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h5 id=&quot;test-setup&quot;&gt;Test setup&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a href=&quot;https://roots.io/bedrock/&quot;&gt;Bedrock&lt;/a&gt; edition of WordPress, which makes WP compatible with PaaS hosting, on top of providing modern tooling (configuration through environment variables, WordPress plugins managed with &lt;code&gt;composer&lt;/code&gt;…)&lt;/li&gt;
&lt;li&gt;PHP 7.4.x&lt;/li&gt;
&lt;li&gt;Site 1
&lt;ul&gt;
&lt;li&gt;WordPress 5.6.x&lt;/li&gt;
&lt;li&gt;List of dependencies and plugins: &lt;a href=&quot;https://gist.github.com/tristanbes/b2b1f0ecbc83313cb3d5c1dc18555dfd&quot;&gt;composer.json&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pm.max_children&lt;/code&gt; is set to &lt;code&gt;10&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><dc:creator>Tristan Bessoussa</dc:creator><dc:language>en</dc:language><category>PaaS</category><category>Clever Cloud</category><category>Performances</category><category>Scalingo</category><category>Hosting</category><category>Cloud</category></item><item><title>French PaaS providers are in a boat: Platform.sh lies at anchor</title><link>https://devops-life.com/en/blog/2019/01/23/platform-sh-paas-review/</link><guid isPermaLink="true">https://devops-life.com/en/blog/2019/01/23/platform-sh-paas-review/</guid><description>This post is part of a series sharing hands-on feedback on French PaaS providers, with one problem in mind: hosting a fleet of 120+ WordPress sites.</description><pubDate>Wed, 23 Jan 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This post is part of a series sharing hands-on feedback on French PaaS providers, with one problem in mind: hosting a fleet of 120+ WordPress sites.&lt;/p&gt;
&lt;p&gt;In this series:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Part 1. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/19/french-paas-for-wordpress-comparison/&quot;&gt;Background to the French PaaS comparison&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 2. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/20/ovh-cloud-web-paas-review/&quot;&gt;PaaS comparison: OVH falls overboard&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 3. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/21/clever-cloud-paas-review/&quot;&gt;PaaS comparison: Clever Cloud, &lt;del&gt;the boat is taking on water&lt;/del&gt; the captain rights the ship&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 4. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/22/scalingo-paas-review/&quot;&gt;PaaS comparison: Scalingo stays afloat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 5. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/23/platform-sh-paas-review/&quot;&gt;PaaS comparison: Platform.sh lies at anchor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 6. &lt;a href=&quot;https://devops-life.com/en/blog/2021/01/19/swiss-paas-hidora-review/&quot;&gt;PaaS comparison: Hidora, the Swiss cloud&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 7. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/24/clever-cloud-vs-scalingo/&quot;&gt;PaaS comparison: performance, Clever Cloud VS Scalingo VS Hidora&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;platformsh&quot;&gt;Platform.sh&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;#r%C3%A9sum%C3%A9-platformsh-tldr&quot;&gt;Skip to the summary if you’re in a hurry&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Our custom Symfony projects are hosted at &lt;a href=&quot;https://platform.sh/&quot;&gt;Platform.sh&lt;/a&gt; (~7 projects).&lt;/p&gt;
&lt;p&gt;I quickly ruled it out for hosting 120+ WordPress projects, because the pricing doesn’t fit our situation at all.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;€40 for 0.8GB of memory&lt;/strong&gt;, and that memory is shared between the application containers. So 800MB of memory split between the web container and the database container: not exactly the stuff of dreams…&lt;/p&gt;
&lt;figure class=&quot;image&quot;&gt;&lt;a href=&quot;https://devops-life.com/img/platformsh-memory.png&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;img src=&quot;https://devops-life.com/img/platformsh-memory.png&quot; alt=&quot;Memory allocation across Platform.sh plans. For a service with PHP and a database, the S plan won&apos;t be enough.&quot; loading=&quot;lazy&quot; width=&quot;610&quot; height=&quot;406&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;figcaption&gt;Memory allocation across Platform.sh plans. For a service with PHP and a database, the S plan won&apos;t be enough.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;The next plan up is €100/month. At that point, we’re multiplying our spending by 10, even though, in our case, we had no use for the feature that lets you run different environments.&lt;/p&gt;
&lt;p&gt;Managing environment variables is, how shall I put it… complicated. You’d think the variables tab on the branch in question would be enough? Actually, no: those variables are only available at runtime, not at build time.&lt;/p&gt;
&lt;p&gt;To add build-time variables, you have to click on a 20 by 20 pixel area that isn’t highlighted at all, then add the variables there. So you often end up adding the variables in the project configuration, then duplicating them in the master environment’s configuration so that the other branches inherit them.&lt;/p&gt;
&lt;p&gt;Note that there’s no “Bulk Edit/Add” mode for environment variables.&lt;/p&gt;
&lt;p&gt;The UI and UX/DX need work, because the interface is a regular source of frustration (on top of being slow).&lt;/p&gt;
&lt;p&gt;Incidents are frequent (or else I’m just unlucky, and on the rare occasions I need Platform.sh, there’s an incident…), along the lines of “huh, my builds are stuck” or “the application isn’t auto-deployed anymore, so the fixes I thought went to prod 7 days ago never made it”. Fortunately, production never went down on our projects, but these recurring problems when using the PaaS wear you down.&lt;/p&gt;
&lt;p&gt;To illustrate: from December to January, there were 10 incidents listed on their &lt;a href=&quot;https://status.platform.sh/history&quot;&gt;status page&lt;/a&gt;, some of them major (not counting maintenance). As I write this post, my team tells me they’re still blocked from deploying some environments.&lt;/p&gt;
&lt;h3 id=&quot;platformsh-summary-tldr&quot;&gt;&lt;span id=&quot;résumé-platformsh-tldr&quot;&gt;&lt;/span&gt;Platform.sh summary (TL;DR)&lt;/h3&gt;
&lt;h4 id=&quot;-cons&quot;&gt;👎 Cons&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Expensive&lt;/li&gt;
&lt;li&gt;No automatic scaling&lt;/li&gt;
&lt;li&gt;No way of knowing how many resources you’re using&lt;/li&gt;
&lt;li&gt;Very slow, unsexy interface&lt;/li&gt;
&lt;li&gt;Environment variable management&lt;/li&gt;
&lt;li&gt;Recurring incidents&lt;/li&gt;
&lt;li&gt;A deployment hook that you regularly have to delete and re-add to keep the project deploying automatically&lt;/li&gt;
&lt;li&gt;No monitoring tool in the interface for creating alert rules (e.g. post a message in Slack if RAM &amp;gt;90%). There’s only a disk space check&lt;/li&gt;
&lt;li&gt;Backups aren’t automated, or even offered as an automatic option. You have to set up a snapshot cron yourself&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;-pros&quot;&gt;👍 Pros&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;You can easily deploy branches as environments for demos or staging&lt;/li&gt;
&lt;li&gt;Environment variable inheritance, which makes it easier to create other environments&lt;/li&gt;
&lt;li&gt;The production database is copied when a new environment is created&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;⛔️  &lt;strong&gt;VERDICT: ELIMINATED&lt;/strong&gt; ⛔️&lt;/p&gt;
&lt;p&gt;I didn’t run any WordPress benchmarks, since the projected cost of migrating to them was already a dealbreaker.&lt;/p&gt;
&lt;h2 id=&quot;read-part-vi&quot;&gt;Read part VI&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Part 6. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/24/clever-cloud-vs-scalingo/&quot;&gt;PaaS comparison: performance, Clever Cloud VS Scalingo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><dc:creator>Tristan Bessoussa</dc:creator><dc:language>en</dc:language><category>PaaS</category><category>Platform.sh</category><category>Performances</category><category>Hosting</category><category>Cloud</category></item><item><title>French PaaS providers are in a boat: Scalingo stays afloat</title><link>https://devops-life.com/en/blog/2019/01/22/scalingo-paas-review/</link><guid isPermaLink="true">https://devops-life.com/en/blog/2019/01/22/scalingo-paas-review/</guid><description>This series of posts shares hands-on feedback on French PaaS providers, with one problem in mind: hosting a fleet of 120+ WordPress sites.</description><pubDate>Tue, 22 Jan 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This series of posts shares hands-on feedback on French PaaS providers, with one problem in mind: hosting a fleet of 120+ WordPress sites.&lt;/p&gt;
&lt;p&gt;In this series:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Part 1. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/19/french-paas-for-wordpress-comparison/&quot;&gt;Background to the French PaaS comparison&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 2. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/20/ovh-cloud-web-paas-review/&quot;&gt;PaaS comparison: OVH falls overboard&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 3. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/21/clever-cloud-paas-review/&quot;&gt;PaaS comparison: Clever Cloud, &lt;del&gt;the boat is taking on water&lt;/del&gt; the captain rights the ship&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 4. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/22/scalingo-paas-review/&quot;&gt;PaaS comparison: Scalingo stays afloat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 5. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/23/platform-sh-paas-review/&quot;&gt;PaaS comparison: Platform.sh lies at anchor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 6. &lt;a href=&quot;https://devops-life.com/en/blog/2021/01/19/swiss-paas-hidora-review/&quot;&gt;PaaS comparison: Hidora, the Swiss cloud&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 7. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/24/clever-cloud-vs-scalingo/&quot;&gt;PaaS comparison: performance, Clever Cloud VS Scalingo VS Hidora&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;scalingo&quot;&gt;Scalingo&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;#r%C3%A9sum%C3%A9-clever-cloud-tldr&quot;&gt;Skip to the summary if you’re in a hurry&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://sclng.io/r/dfa62f9bc47ec843&quot;&gt;Scalingo&lt;/a&gt; is a PaaS I had never heard of (not even on my Twitter timeline), so I added it to my list of “candidates” somewhat reluctantly.&lt;/p&gt;
&lt;p&gt;Here, onboarding is very simple. I didn’t have to go and negotiate a budget just to test the service: &lt;strong&gt;everything is billed by the minute&lt;/strong&gt;. That alone makes every service that bills by the month look badly dated.&lt;/p&gt;
&lt;p&gt;If your site gets featured on Capital, the prime-time French TV show, you can order XX extra scalers so the application handles the load, and only get billed for the 20 minutes during which the segment talks about you. The same goes for the database, to raise the infamous concurrent connection limits (though in that case, you’d be better off switching the site to read-only and putting a reverse proxy in front of it) and the RAM.&lt;/p&gt;
&lt;p&gt;The interface isn’t in step with current trends: it looks a bit old-fashioned, but it’s pleasant to use and very snappy. Better that than a very “pretty” interface that’s poorly thought out and slow.&lt;/p&gt;
&lt;h4 id=&quot;building-the-application-on-scalingo&quot;&gt;Building the application on Scalingo&lt;/h4&gt;
&lt;p&gt;You need to create a &lt;code&gt;.buildpacks&lt;/code&gt; file at the root of the project, containing the URL of a Git repository that tells the PaaS how to build the application. In our case, it was: &lt;code&gt;https://github.com/Scalingo/php-buildpack&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;composer.json&lt;/code&gt; is then detected and, &lt;strong&gt;bonus&lt;/strong&gt;, since they support the Bedrock edition of WordPress, I didn’t have to do anything to configure the NGINX vhost: it just worked (&lt;a href=&quot;https://github.com/Scalingo/php-buildpack/tree/master/frameworks&quot;&gt;list of auto-detected PHP frameworks&lt;/a&gt;). That wasn’t the case at Clever Cloud, for example, where I had to specify where the application’s front controller was (&lt;code&gt;web/index.php&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;The application builds quickly, and I was able to move on to the tests.&lt;/p&gt;
&lt;h4 id=&quot;performance-at-scalingo&quot;&gt;Performance at Scalingo&lt;/h4&gt;
&lt;p&gt;The tests will speak for themselves in the dedicated chapter. Note that &lt;a href=&quot;https://sclng.io/r/dfa62f9bc47ec843&quot;&gt;Scalingo&lt;/a&gt; crushes Clever Cloud, which doesn’t even pass some of the tests at an equivalent price.&lt;/p&gt;
&lt;p&gt;A few times, we got much better performance at Scalingo on tests run on different dates. According to support, this comes down to the neighbors: scalers can move from machine to machine depending on their load. If we have neighbors whose scalers have a very high CPU priority (so they pay more) and who happen to be very busy at that moment, then less CPU time gets allocated to our lower-priority containers.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Part 7. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/24/clever-cloud-vs-scalingo/&quot;&gt;PaaS comparison: performance, Clever Cloud VS Scalingo VS Hidora&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;scalingo-summary-tldr&quot;&gt;&lt;span id=&quot;résumé-clever-cloud-tldr&quot;&gt;&lt;/span&gt;Scalingo summary (TL;DR)&lt;/h3&gt;
&lt;h4 id=&quot;-cons&quot;&gt;👎 Cons&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;The UI isn’t very sexy, though it’s not a disaster either.&lt;/li&gt;
&lt;li&gt;Autoscaling (in closed beta) that still needs a bit of fine-tuning&lt;/li&gt;
&lt;li&gt;The log management interface could be improved visually&lt;/li&gt;
&lt;li&gt;No GitHub Server support (for the deployment app)&lt;/li&gt;
&lt;li&gt;No “big picture” view (yet) for portfolios with lots of projects&lt;/li&gt;
&lt;li&gt;No reverse proxy (e.g. Varnish) that’s easy to configure from a &lt;code&gt;.vcl &lt;/code&gt; file dropped at the root of the application, to boost application performance by taking advantage of the HTTP cache&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;EDIT 12/20&lt;/strong&gt; At least 2 major platform incidents in 2020, following &lt;a href=&quot;https://scalingo.com/blog/mitigating-massive-ddos&quot;&gt;DDoS&lt;/a&gt; attacks&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;-pros&quot;&gt;👍 Pros&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Competitive pricing&lt;/li&gt;
&lt;li&gt;Everything is billed by the “minute”, even the database&lt;/li&gt;
&lt;li&gt;Horizontal auto-scaling&lt;/li&gt;
&lt;li&gt;Responsive support. &lt;strong&gt;EDIT 12/3/20&lt;/strong&gt; Still very responsive, and there’s often a developer on the other end who gets the problem right away and comes up with a solution quickly. That’s much appreciated.&lt;/li&gt;
&lt;li&gt;Pleasant, snappy interface (UX/DX)&lt;/li&gt;
&lt;li&gt;Easy control of &lt;code&gt;pm.max_children&lt;/code&gt; from the UI (the &lt;code&gt;MAX_CONCURRENCY&lt;/code&gt; variable)&lt;/li&gt;
&lt;li&gt;Separate environments for the same application (prod/staging…), though not as easy as what Platform.sh offers, for example&lt;/li&gt;
&lt;li&gt;Simple, fairly complete monitoring rules for your scalers, across several channels (Slack, email…)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;EDIT 12/3/20&lt;/strong&gt; A more flexible environment than some PaaS providers when it comes to certain extensions: compatible with Blackfire, Datadog…&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;-verdict-selected-&quot;&gt;✅ &lt;strong&gt;VERDICT: SELECTED&lt;/strong&gt; ✅&lt;/h4&gt;
&lt;p&gt;For me, &lt;a href=&quot;https://sclng.io/r/dfa62f9bc47ec843&quot;&gt;Scalingo&lt;/a&gt; was &lt;strong&gt;THE&lt;/strong&gt; pleasant surprise of this comparison. Its pricing is competitive for the market, with no compromise on performance. The main features you’d expect from a PaaS are there, and they work well!&lt;/p&gt;
&lt;h2 id=&quot;read-part-v&quot;&gt;Read part V&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Part 5. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/23/platform-sh-paas-review/&quot;&gt;PaaS comparison: Platform.sh&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;u&gt;Disclaimer:&lt;/u&gt; the Scalingo links use a referral program tied to my personal account. If the other PaaS providers tested here had offered the same kind of referral program, I would have used it for their links too (for future POCs or small side projects).&lt;/p&gt;
</content:encoded><dc:creator>Tristan Bessoussa</dc:creator><dc:language>en</dc:language><category>PaaS</category><category>Performances</category><category>Scalingo</category><category>Hosting</category><category>Cloud</category></item><item><title>French PaaS providers are in a boat: Clever Cloud rights the ship</title><link>https://devops-life.com/en/blog/2019/01/21/clever-cloud-paas-review/</link><guid isPermaLink="true">https://devops-life.com/en/blog/2019/01/21/clever-cloud-paas-review/</guid><description>This series of posts shares hands-on feedback on French PaaS providers, with one problem in mind: hosting a fleet of 120+ WordPress sites.</description><pubDate>Mon, 21 Jan 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This series of posts shares hands-on feedback on French PaaS providers, with one problem in mind: hosting a fleet of 120+ WordPress sites.&lt;/p&gt;
&lt;p&gt;In this series:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Part 1. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/19/french-paas-for-wordpress-comparison/&quot;&gt;Background to the French PaaS comparison&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 2. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/20/ovh-cloud-web-paas-review/&quot;&gt;PaaS comparison: OVH falls overboard&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 3. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/21/clever-cloud-paas-review/&quot;&gt;PaaS comparison: Clever Cloud, &lt;del&gt;the boat is taking on water&lt;/del&gt; the captain rights the ship&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 4. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/22/scalingo-paas-review/&quot;&gt;PaaS comparison: Scalingo stays afloat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 5. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/23/platform-sh-paas-review/&quot;&gt;PaaS comparison: Platform.sh lies at anchor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 6. &lt;a href=&quot;https://devops-life.com/en/blog/2021/01/19/swiss-paas-hidora-review/&quot;&gt;PaaS comparison: Hidora, the Swiss cloud&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 7. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/24/clever-cloud-vs-scalingo/&quot;&gt;PaaS comparison: performance, Clever Cloud VS Scalingo VS Hidora&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;clever-cloud&quot;&gt;Clever Cloud&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Update, January 2021&lt;/strong&gt;: during 2020, Clever Cloud changed its database pricing, which considerably reduces the costs I talked about in this post. &lt;br&gt;
What’s more, after a new performance test using the same project as in December 2019, default performance is much better than before (see the update to &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/24/clever-cloud-vs-scalingo/&quot;&gt;PaaS comparison: performance, Clever Cloud VS Scalingo&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;#r%C3%A9sum%C3%A9-clever-cloud-tldr&quot;&gt;Skip to the summary if you’re in a hurry&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.clever-cloud.com/&quot;&gt;Clever Cloud&lt;/a&gt; is another French PaaS that kept popping up in my Twitter timeline whenever people asked where to host their projects. So I was keen to try it out.&lt;/p&gt;
&lt;p&gt;On paper, the offering is sexy: vertical and horizontal (auto)scaling.&lt;/p&gt;
&lt;h4 id=&quot;the-database&quot;&gt;The database&lt;/h4&gt;
&lt;p&gt;When you sign up, first bad surprise: the database is billed by the full month, and it can’t be scaled yet, either automatically or manually, though that should change (soon).&lt;/p&gt;
&lt;div class=&quot;table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Table&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Clever Cloud&lt;/th&gt;
&lt;th&gt;Scalingo&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Max connections&lt;/td&gt;
&lt;td&gt;75&lt;/td&gt;
&lt;td&gt;62&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max DB size&lt;/td&gt;
&lt;td&gt;&lt;del&gt;10&lt;/del&gt; 5 GB&lt;/td&gt;
&lt;td&gt;5 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory&lt;/td&gt;
&lt;td&gt;1 GB&lt;/td&gt;
&lt;td&gt;1 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Type&lt;/td&gt;
&lt;td&gt;Dedicated&lt;/td&gt;
&lt;td&gt;Dedicated (Software + RAM)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU&lt;/td&gt;
&lt;td&gt;1 vCPU&lt;/td&gt;
&lt;td&gt;Shared&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Price&lt;/td&gt;
&lt;td&gt;&lt;del&gt;€45&lt;/del&gt; €22/month &lt;br /&gt;&lt;/td&gt;
&lt;td&gt;€14.4/month&lt;br /&gt;(billed by the minute)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Another red flag: &lt;a href=&quot;https://www.clever-cloud.com/&quot;&gt;Clever Cloud&lt;/a&gt; openly nudges you toward PostgreSQL, since for strictly equivalent resources, database containers are &lt;strong&gt;more expensive&lt;/strong&gt; for MySQL.&lt;/p&gt;
&lt;p&gt;We’re talking about a &lt;strong&gt;difference of &lt;del&gt;€180&lt;/del&gt; +€54 a year&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;They justify the difference by saying that a MySQL instance costs more to run than PostgreSQL, and their tech team prefers maintaining PostgreSQL.&lt;/p&gt;
&lt;p&gt;For a Symfony application, switching to PostgreSQL makes no difference to me (or almost…), but with WordPress, we have no say in which database the CMS supports. So in the end, it’s the user who pays the extra cost.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;EDIT 12/20&lt;/strong&gt; &lt;del&gt;Apparently, they’re reworking the pricing of their database scalers.&lt;/del&gt; That’s now done, hence this update to the post.&lt;/p&gt;
&lt;h4 id=&quot;configuring-the-application-on-clever-cloud&quot;&gt;Configuring the application on Clever Cloud&lt;/h4&gt;
&lt;p&gt;As with any application hosted on a PaaS, you go through the “environment variables” step. At Clever Cloud, it’s not very convenient. When I ran my tests a few weeks ago, there was no way to edit or add variables in bulk. When you have 20 variables to enter, what a waste of time! Fortunately, that was fixed recently with the addition of a bulk edit/add.&lt;/p&gt;
&lt;p&gt;You can’t reference another variable (e.g. &lt;code&gt;DATABASE_URL=$MYSQL_ADDON_URI&lt;/code&gt;), which is a shame.&lt;/p&gt;
&lt;p&gt;Next, I wanted to deploy my WordPress. Unfortunately I couldn’t, because a dependency (&lt;a href=&quot;https://github.com/wp-cli/wp-cli&quot;&gt;wp-cli&lt;/a&gt;) required the PHP extension &lt;code&gt;ext-readline&lt;/code&gt;, which wasn’t available on Clever Cloud.&lt;/p&gt;
&lt;p&gt;I had to ask support, and as luck would have it, it was a Friday, so no production release possible before I left on vacation.&lt;/p&gt;
&lt;p&gt;&lt;u&gt;NB (not aimed at Clever Cloud):&lt;/u&gt; this absurd policy has to stop. I fully agree with the tweet below: at our company, Friday is a day like any other for deployments, because we made sure it would be (tests, automation, reviews…).&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot; data-lang=&quot;en&quot; lang=&quot;en&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;&amp;quot;No deploys on friday&amp;quot; is cancer philosophy that enforces fear to deploy and slow down the dev cycle. Stop repeating this crap as if it&amp;#39;s cool, and improve processes and tests, for god&amp;#39;s sake.&lt;/p&gt;&amp;mdash; SergiGP 🎗 (@SergiGP) &lt;a href=&quot;https://twitter.com/SergiGP/status/1075417087714181120?ref_src=twsrc%5Etfw&quot;&gt;December 19, 2018&lt;/a&gt;&lt;/blockquote&gt;
&lt;p&gt;When I got back from vacation, I still couldn’t build because of a mix-up: “the feature is enabled but not the extension”. Anyway, it came through a few hours later: the application could finally be built.&lt;/p&gt;
&lt;h4 id=&quot;building-the-application-on-clever-cloud&quot;&gt;Building the application on Clever Cloud&lt;/h4&gt;
&lt;p&gt;So, how do you build on their platform?&lt;/p&gt;
&lt;p&gt;Since the project has a &lt;code&gt;composer.json&lt;/code&gt;, the PaaS fetches the dependencies automatically. But we build the theme with a &lt;code&gt;yarn build&lt;/code&gt; task, so we need to be able to run a &lt;code&gt;yarn install&lt;/code&gt; followed by a &lt;code&gt;yarn build&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;And that’s where it gets awkward, because it can’t work out “OK, I see a &lt;code&gt;yarn.lock&lt;/code&gt;, so I’ll also run a &lt;code&gt;yarn install &amp;amp;&amp;amp; yarn build&lt;/code&gt;”. So you have to go through a &lt;strong&gt;Build Hook.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You set a variable telling it to use &lt;code&gt;yarn&lt;/code&gt; as the package manager, then you tell it which commands to run and at which point of the build, for example: &lt;code&gt;CC_POST_BUILD_HOOK=&quot;yarn install &amp;amp;&amp;amp; yarn build&quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Note that WordPress wasn’t detected automatically: I had to create a file named &lt;code&gt;php.json&lt;/code&gt; in a &lt;code&gt;.clevercloud&lt;/code&gt; folder so the router would know what to serve:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes devops-life-light devops-life-dark&quot; style=&quot;--shiki-light:#1B2638;--shiki-dark:#DAE2EC;--shiki-light-bg:#FFFFFF;--shiki-dark-bg:#151C26; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;deploy&quot;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;webroot&quot;: &quot;/web&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  &quot;configuration&quot;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    &quot;pm.max_children&quot;: &quot;20&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;performance-at-clever-cloud&quot;&gt;Performance at Clever Cloud&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;EDIT 01/21&lt;/strong&gt; The new pricing policies, along with many hardware/software improvements (datacenter migration + a better reverse proxy written in Rust), have let Clever Cloud drastically right the ship compared with the tests run in December 2019.&lt;/p&gt;
&lt;p&gt;So the price gap is narrowing. My tests from January 2021 show that you no longer have to spend 5 to 9 times more money to get performance similar to one of its main competitors, Scalingo.
It’s now closer to a x2 ratio. In other words: on our WordPress setup, Scalingo matches Clever Cloud’s performance for half the price.&lt;/p&gt;
&lt;p&gt;Example: I send 5 unique visitors per second to the home page for 2 minutes.&lt;/p&gt;
&lt;p&gt;For &lt;strong&gt;€10.8&lt;/strong&gt;/month at &lt;u&gt;Scalingo&lt;/u&gt;, I get an average response time of &lt;strong&gt;492ms&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;For &lt;strong&gt;€20.3&lt;/strong&gt;/month at &lt;u&gt;Clever Cloud&lt;/u&gt;, I get &lt;strong&gt;588ms&lt;/strong&gt;. That’s the &lt;u&gt;minimum&lt;/u&gt; you have to spend to get similar performance.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Part 7. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/24/clever-cloud-vs-scalingo/&quot;&gt;PaaS comparison: performance, Clever Cloud VS Scalingo VS Hidora&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;clever-cloud-summary-tldr&quot;&gt;&lt;span id=&quot;résumé-clever-cloud-tldr&quot;&gt;&lt;/span&gt;Clever Cloud summary (TL;DR)&lt;/h3&gt;
&lt;h4 id=&quot;-cons&quot;&gt;👎 Cons&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;del&gt;Disastrous price/performance ratio on WordPress&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;Database add-on pricing: &lt;del&gt;expensive, and&lt;/del&gt; monthly commitment&lt;/li&gt;
&lt;li&gt;No database scaling&lt;/li&gt;
&lt;li&gt;&lt;del&gt;No references to other environment variables&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;No GitHub Server support for automatic deployment&lt;/li&gt;
&lt;li&gt;Interface sometimes unintuitive&lt;/li&gt;
&lt;li&gt;Statistics (in BETA since 2019?)&lt;/li&gt;
&lt;li&gt;No easy way to set &lt;code&gt;pm.max_children&lt;/code&gt; from the UI (e.g. through an environment variable)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;EDIT 12/20&lt;/strong&gt; Very little openness to the PHP ecosystem: APM / monitoring (no Datadog, blackfire.io, tideways.io)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;EDIT 01/21&lt;/strong&gt; Good price/performance ratio&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;-pros&quot;&gt;👍 Pros&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Automated backups&lt;/li&gt;
&lt;li&gt;Let’s Encrypt integration&lt;/li&gt;
&lt;li&gt;Vertical and/or horizontal auto-scaling&lt;/li&gt;
&lt;li&gt;Support that’s responsive when it comes to communication&lt;/li&gt;
&lt;li&gt;Sexy, snappy interface&lt;/li&gt;
&lt;li&gt;Clear log management&lt;/li&gt;
&lt;li&gt;A home-grown alternative to Amazon S3, for mounting persistent storage&lt;/li&gt;
&lt;li&gt;An optional reverse proxy (Varnish) that looks simple to manage&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;EDIT 12/20&lt;/strong&gt; Very stable (feedback from 1+ year of hosting a project with them)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;EDIT 12/20&lt;/strong&gt; Multi-region support (with OVH’s help behind the scenes)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;️-verdict-eliminated-️&quot;&gt;⛔️ &lt;strong&gt;VERDICT: ELIMINATED&lt;/strong&gt; ⛔️&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;EDIT 01/21&lt;/strong&gt; Performance and price, the 2 problems that got Clever Cloud eliminated for our hosting needs in 2019, are now solved.&lt;/p&gt;
&lt;h2 id=&quot;read-part-iv&quot;&gt;Read part IV&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Part 4. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/22/scalingo-paas-review/&quot;&gt;PaaS comparison: Scalingo, staying afloat&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><dc:creator>Tristan Bessoussa</dc:creator><dc:language>en</dc:language><category>PaaS</category><category>Performances</category><category>Clever Cloud</category><category>Hosting</category><category>Cloud</category></item><item><title>French PaaS providers are in a boat: OVH falls overboard</title><link>https://devops-life.com/en/blog/2019/01/20/ovh-cloud-web-paas-review/</link><guid isPermaLink="true">https://devops-life.com/en/blog/2019/01/20/ovh-cloud-web-paas-review/</guid><description>This post is part of a series sharing hands-on feedback on French PaaS providers, with one problem in mind: hosting a fleet of 120+ WordPress sites.</description><pubDate>Sun, 20 Jan 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This post is part of a series sharing hands-on feedback on French PaaS providers, with one problem in mind: hosting a fleet of 120+ WordPress sites.&lt;/p&gt;
&lt;p&gt;In this series:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Part 1. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/19/french-paas-for-wordpress-comparison/&quot;&gt;Background to the French PaaS comparison&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 2. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/20/ovh-cloud-web-paas-review/&quot;&gt;PaaS comparison: OVH falls overboard&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 3. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/21/clever-cloud-paas-review/&quot;&gt;PaaS comparison: Clever Cloud, &lt;del&gt;the boat is taking on water&lt;/del&gt; the captain rights the ship&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 4. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/22/scalingo-paas-review/&quot;&gt;PaaS comparison: Scalingo stays afloat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 5. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/23/platform-sh-paas-review/&quot;&gt;PaaS comparison: Platform.sh lies at anchor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 6. &lt;a href=&quot;https://devops-life.com/en/blog/2021/01/19/swiss-paas-hidora-review/&quot;&gt;PaaS comparison: Hidora, the Swiss cloud&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 7. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/24/clever-cloud-vs-scalingo/&quot;&gt;PaaS comparison: performance, Clever Cloud VS Scalingo VS Hidora&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;ovh-and-its-cloud-web-offering&quot;&gt;OVH and its Cloud Web offering&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;#r%C3%A9sum%C3%A9-ovh-cloud-web-tldr&quot;&gt;Skip to the summary if you’re in a hurry&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;OVH is the latest arrival in the PaaS world with its &lt;a href=&quot;https://www.ovh.com/fr/hebergement-web/cloud-web.xml&quot;&gt;Cloud Web offering&lt;/a&gt;. It’s not their area of expertise, and you can tell quickly. Very quickly.&lt;/p&gt;
&lt;p&gt;First bad surprise: you can’t order a Cloud Web plan for less than 12 months.&lt;/p&gt;
&lt;p&gt;If you don’t want to order a domain name with the Cloud Web plan (because you already have one), there’s no way to skip that step when ordering through the OVH web interface. You can, however, get around it by ordering the plan through the OVH API.&lt;/p&gt;
&lt;p&gt;Apparently the teams are working on lifting these restrictions, but according to an internal source, it won’t happen for many months (no guarantees). But let’s be clear: some competitors offer &lt;strong&gt;per-minute billing&lt;/strong&gt;, so shelling out ~€490 including VAT just to run a few tests on Cloud Web 3 is hard to justify. And if we project that onto our fleet of 120+ WordPress projects, it would mean an entry ticket of ~&lt;strong&gt;€60,000&lt;/strong&gt;, with no guarantee that our clients stay with us for a year.&lt;/p&gt;
&lt;p&gt;I got in touch with someone at OVH to report these issues. They offered me Cloud Web 3 free for 1 year in exchange for feedback on the product. Thanks for the offer.&lt;/p&gt;
&lt;p&gt;As soon as I had access to the product, I wanted to switch to PHP 7.3 and create my environment variables so I could configure WordPress and run my performance tests. But I ran into problems, one of which stopped me from using the product at all:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PHP 7.3 isn’t available, even though it came out on December 6, 2018, more than a month before this post was written&lt;/li&gt;
&lt;li&gt;No permission management (other than for the admin/technical contact), for example to let a group of people on a team manage environment variables&lt;/li&gt;
&lt;li&gt;I created some environment variables: some were created within seconds or minutes, while others still said “Being created” after 15 minutes. I dug through the UI before finding out that the creation task for some of them had failed (with no further details)&lt;/li&gt;
&lt;li&gt;No “bulk insert” for environment variables. Because of that, I had to repeat the variable creation process 20 times&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;image&quot;&gt;&lt;a href=&quot;https://devops-life.com/img/ovh-environment-variables-bug.png&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;img src=&quot;https://devops-life.com/img/ovh-environment-variables-bug.png&quot; alt=&quot;The creation of these OVH environment variables has been &apos;in progress&apos; for 14 days, with no way to delete or edit them&quot; loading=&quot;lazy&quot; width=&quot;610&quot; height=&quot;302&quot; decoding=&quot;async&quot; /&gt;&lt;/a&gt;&lt;figcaption&gt;The creation of these OVH environment variables has been &apos;in progress&apos; for 14 days, with no way to delete or edit them&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h4 id=&quot;ovh-support&quot;&gt;OVH support&lt;/h4&gt;
&lt;p&gt;With no way to edit or delete the “faulty” variables, &lt;strong&gt;I’m stuck&lt;/strong&gt; in the middle of my setup, so I turn to support.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;January 8, 2019: I open a support ticket to report that I can’t use the product because of my environment variable problem&lt;/li&gt;
&lt;li&gt;January 21, 2019, &lt;strong&gt;13 DAYS later&lt;/strong&gt;: I get the first reply from support, which boils down to: “We apologize for the delay in handling your ticket; our administrators are looking into your request.”&lt;/li&gt;
&lt;li&gt;January 23, 2019: I’m told that the failed operations have been canceled and invited to try again, with no explanation of what happened.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;ovh-cloud-web-summary-tldr&quot;&gt;&lt;span id=&quot;résumé-ovh-cloud-web-tldr&quot;&gt;&lt;/span&gt;OVH Cloud Web summary (TL;DR)&lt;/h4&gt;
&lt;h4 id=&quot;-cons&quot;&gt;👎 Cons&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Annual commitment&lt;/li&gt;
&lt;li&gt;No monthly payment&lt;/li&gt;
&lt;li&gt;Can’t order without an associated domain through the web interface&lt;/li&gt;
&lt;li&gt;Very thin, incomplete documentation&lt;/li&gt;
&lt;li&gt;No Git deployment&lt;/li&gt;
&lt;li&gt;No automatic deployment via a GitHub/GitLab app&lt;/li&gt;
&lt;li&gt;No horizontal or vertical scaling&lt;/li&gt;
&lt;li&gt;No &lt;a href=&quot;https://en.wikipedia.org/wiki/Autoscaling&quot;&gt;auto-scaling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;OVH support: steer clear&lt;/li&gt;
&lt;li&gt;No permission management, for example to give a team access to environment variables&lt;/li&gt;
&lt;li&gt;Poor UX/DX for managing variables, on top of being buggy&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;-pros&quot;&gt;👍 Pros&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Automated backups&lt;/li&gt;
&lt;li&gt;Let’s Encrypt integration&lt;/li&gt;
&lt;li&gt;OVH network&lt;/li&gt;
&lt;li&gt;That’s about it… 🤷‍♂️😱&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;️-verdict-eliminated-️&quot;&gt;⛔️ &lt;strong&gt;VERDICT: ELIMINATED&lt;/strong&gt; ⛔️&lt;/h4&gt;
&lt;p&gt;It’s hard to pass final judgment on OVH’s PaaS offerings when you can feel they still need to mature before they meet the market’s needs. Honestly, I was frustrated and disappointed that I couldn’t benchmark the performance of their Cloud Web 3 plan.&lt;/p&gt;
&lt;p&gt;Unfortunately, my preconceptions about their support were confirmed, so I can’t recommend this offering to anyone for professional use: at the slightest problem, you wait weeks for support to reply (unless you hound them on Twitter), even when it’s critical for your service.&lt;/p&gt;
&lt;p&gt;As for personal use, who can drop ~€490 in one go to host their personal “side project” on a PaaS? 🙊&lt;/p&gt;
&lt;h2 id=&quot;read-part-iii&quot;&gt;Read part III&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Part 3. &lt;a href=&quot;https://devops-life.com/en/blog/2019/01/21/clever-cloud-paas-review/&quot;&gt;PaaS comparison: Clever Cloud, the boat is taking on water&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><dc:creator>Tristan Bessoussa</dc:creator><dc:language>en</dc:language><category>PaaS</category><category>Performances</category><category>OVH</category><category>Hosting</category><category>Cloud</category></item></channel></rss>