<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rubén Rubio</title>
    <description>The latest articles on DEV Community by Rubén Rubio (@rubenrubiob).</description>
    <link>https://dev.to/rubenrubiob</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F627067%2Fbcee1b96-68f6-48f2-b7a5-26dc76f9fb42.jpeg</url>
      <title>DEV Community: Rubén Rubio</title>
      <link>https://dev.to/rubenrubiob</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rubenrubiob"/>
    <language>en</language>
    <item>
      <title>Weekly reading digest #31</title>
      <dc:creator>Rubén Rubio</dc:creator>
      <pubDate>Sun, 17 Mar 2024 08:30:00 +0000</pubDate>
      <link>https://dev.to/rubenrubiob/weekly-reading-digest-31-2685</link>
      <guid>https://dev.to/rubenrubiob/weekly-reading-digest-31-2685</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/Girgias/php-rfcs/blob/master/container-offset-behaviour.md" rel="noopener noreferrer"&gt;PHP RFC: Improve language coherence for the behaviour of offsets and containers&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/realflowcontrol/processing-one-billion-rows-in-php-3eg0"&gt;Processing One Billion Rows in PHP!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stevegrunwell.com/blog/php-value-objects/" rel="noopener noreferrer"&gt;The Beauty of PHP Value Objects&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://joeymckenzie.tech/blog/exploring-developer-experience-with-php-public-apis-and-beer" rel="noopener noreferrer"&gt;Exploring developer experience with PHP, public APIs, and beer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@vlreshet/stop-using-old-fashioned-closures-in-modern-php-there-are-4-ways-to-replace-them-51d8661e2f7e" rel="noopener noreferrer"&gt;Stop using old-fashioned closures in modern PHP. There are 4* ways to replace them.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.dantleech.com/blog/2024/03/10/why-i-dont-write-adrs/" rel="noopener noreferrer"&gt;Why I don't write ADRs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://spectrum.ieee.org/prompt-engineering-is-dead" rel="noopener noreferrer"&gt;AI Prompt Engineering Is Dead&lt;/a&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>backend</category>
      <category>symfony</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Weekly reading digest #30</title>
      <dc:creator>Rubén Rubio</dc:creator>
      <pubDate>Sun, 10 Mar 2024 09:00:00 +0000</pubDate>
      <link>https://dev.to/rubenrubiob/weekly-reading-digest-30-2h4j</link>
      <guid>https://dev.to/rubenrubiob/weekly-reading-digest-30-2h4j</guid>
      <description>&lt;p&gt;&lt;a href="https://labs.davlgd.fr/posts/2024-03-having-fun-with-franken-php/" rel="noopener noreferrer"&gt;Having fun with (Franken)PHP&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://aaronsaray.com/2021/install-composer-package-from-local-zip/" rel="noopener noreferrer"&gt;How to Install a Composer package from a local zip&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://alanastorm.com/composer-path-repositories/" rel="noopener noreferrer"&gt;Composer Path Repositories&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.oliverdavies.uk/archive/2024/03/06/types-are-optional" rel="noopener noreferrer"&gt;Types are optional&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.oliverdavies.uk/archive/2024/03/05/why-write-framework-agnostic-code" rel="noopener noreferrer"&gt;Why write framework-agnostic code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://compiler.blog/testing-best-practices-the-ultimate-guide" rel="noopener noreferrer"&gt;Testing Best Practices: The Ultimate Guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://winkelwagen.de/2024/03/05/shopware-hard-coded-uuids/" rel="noopener noreferrer"&gt;Shopware: Hard coded UUIDs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/pulse/using-php-attributes-instead-annotations-static-carlos-granados-qanwe/" rel="noopener noreferrer"&gt;Using PHP Attributes instead of Annotations for Static Analysis&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jolicode.com/blog/forcer-le-redemarrage-dun-worker-symfony" rel="noopener noreferrer"&gt;Forcer le redémarrage d’un worker Symfony [In French]&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://symfonystation.mobileatom.net/PHP-Platforms-Symfony" rel="noopener noreferrer"&gt;Exploring the 7 PHP Frameworks using Symfony Components&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/dunglas/symfony-docker/issues/555" rel="noopener noreferrer"&gt;Moving to Debian&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.cognitect.com/blog/2011/11/15/documenting-architecture-decisions" rel="noopener noreferrer"&gt;Documenting Architecture Decisions&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jolicode.com/blog/loption-reference-de-git-clone" rel="noopener noreferrer"&gt;L’option reference de git clone[In French]&lt;/a&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>backend</category>
      <category>symfony</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Weekly reading digest #29</title>
      <dc:creator>Rubén Rubio</dc:creator>
      <pubDate>Sun, 03 Mar 2024 09:00:00 +0000</pubDate>
      <link>https://dev.to/rubenrubiob/weekly-reading-digest-29-575b</link>
      <guid>https://dev.to/rubenrubiob/weekly-reading-digest-29-575b</guid>
      <description>&lt;p&gt;&lt;a href="https://drops-of-php.hi-folks.dev/02-array/01-arr-array_map/" rel="noopener noreferrer"&gt;Mapping an array&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/klnjmm/never-write-another-loop-again-3np0"&gt;Never write another loop again&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://alejandrocelaya.blog/2024/02/23/capturing-remote-code-coverage-in-e2e-tests-with-phpunit/" rel="noopener noreferrer"&gt;Capturing remote code coverage in E2E tests with PHPUnit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@nemanjamilenkovic_58178/rethinking-database-migrations-the-diminishing-role-of-the-down-method-in-php-frameworks-f3f42b5315aa" rel="noopener noreferrer"&gt;Rethinking Database Migrations: The Diminishing Role of the down() Method in PHP Frameworks&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://thephp.foundation/blog/2024/02/26/transparency-and-impact-report-2023/" rel="noopener noreferrer"&gt;The PHP Foundation: Impact and Transparency Report 2023&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://thenextweb.com/news/why-php-continues-to-be-a-popular-but-divisive-programming-language?ref=dailydev" rel="noopener noreferrer"&gt;Why PHP continues to be a popular but divisive programming language&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://sebastiandedeyne.com/choosing-a-frontend-framework" rel="noopener noreferrer"&gt;Choosing a frontend framework&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://acairns.co.uk/posts/composition-over-inheritance" rel="noopener noreferrer"&gt;Composition over Inheritance&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.hillelwayne.com/post/constructive/" rel="noopener noreferrer"&gt;Constructive vs Predicative Data&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.singlestoneconsulting.com/blog/new-miro-template-for-designing-bounded-contexts" rel="noopener noreferrer"&gt;New Miro Template for Designing Bounded Contexts&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://danilat.com/weblog/2024/01/23/hexagonal-product-market-fit" rel="noopener noreferrer"&gt;Buscando el Product Market Fit y Arquitectura Hexagonal [In Spanish]&lt;/a&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>symfony</category>
      <category>backend</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Weekly reading digest #28</title>
      <dc:creator>Rubén Rubio</dc:creator>
      <pubDate>Sun, 25 Feb 2024 09:00:00 +0000</pubDate>
      <link>https://dev.to/rubenrubiob/weekly-reading-digest-28-5124</link>
      <guid>https://dev.to/rubenrubiob/weekly-reading-digest-28-5124</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/honeybadger/a-guide-to-exception-handling-in-php-4cah"&gt;A guide to exception handling in PHP&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.dantleech.com/blog/2024/02/18/my-php-problems/" rel="noopener noreferrer"&gt;My PHP Problems&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jolicode.com/blog/comment-ecrire-une-regle-rector" rel="noopener noreferrer"&gt;Comment écrire une règle Rector&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ted.dev/articles/2023/04/02/i-m-done-with-unit-and-integration-tests/" rel="noopener noreferrer"&gt;I'm Done with Unit and Integration Tests&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.joshwcomeau.com/javascript/statements-vs-expressions/" rel="noopener noreferrer"&gt;Statements Vs. Expressions&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cep.dev/posts/every-infrastructure-decision-i-endorse-or-regret-after-4-years-running-infrastructure-at-a-startup/" rel="noopener noreferrer"&gt;(Almost) Every infrastructure decision I endorse or regret after 4 years running infrastructure at a startup&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dgtlinfra.com/data-center-water-usage/" rel="noopener noreferrer"&gt;Data Center Water Usage: A Comprehensive Guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://aaronfrancis.com/2024/do-literally-anything" rel="noopener noreferrer"&gt;Do literally anything&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://bandini.medium.com/reggaeton-be-gone-6fa55f46a9d7" rel="noopener noreferrer"&gt;Reggaeton Be Gone&lt;/a&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>symfony</category>
      <category>backend</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>The importance of PHP's OPcache</title>
      <dc:creator>Rubén Rubio</dc:creator>
      <pubDate>Wed, 21 Feb 2024 08:30:00 +0000</pubDate>
      <link>https://dev.to/rubenrubiob/the-importance-of-phps-opcache-3p5f</link>
      <guid>https://dev.to/rubenrubiob/the-importance-of-phps-opcache-3p5f</guid>
      <description>&lt;p&gt;&lt;em&gt;Wednesday, 20th of December 2023, 10.08 AM&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;Client&lt;/strong&gt;: "Our PHP + Symfony application has performance issues. The website is slow; it takes a lot of time to load some pages. Sometimes, it just freezes, making it unusable. We are receiving complaints from our users: they need it to work, but they can not."&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Wednesday, 20th of December 2023, 04.57 PM&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;Developer&lt;/strong&gt;: "Hello! That is something I can look into. We can solve it, no problem. However, the next two weeks I am on holidays, so I will work on it when I am back. Merry Christmas and Happy New Year!"&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Monday, 8th of January 2024, 07.43 AM&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;Client&lt;/strong&gt;: "How is the optimization going? My users could not have holidays: they had to make up for the time lost while working with our website. They are starting to get angry. I even got some threats in my physical mailbox."&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Monday, 8th of January 2024, 01.19 PM&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;Developer&lt;/strong&gt;: "First, Happy New Year! I understand your users: it is normal to become irritable when you do not have time off. They should take their job less seriously; they are not going to inherit the company. And for you, just do not look into your mailbox. Who sends letters today, anyway?"&lt;/p&gt;

&lt;p&gt;"But let me take a look at your application. First, we need to have some metrics to know what is going on. We need to observe which endpoints are slow and which ones are called the most... Otherwise, we are just acting on sensations, not on data. To observe the system, I just installed New Relic. We should have enough data to draw conclusions in a week. We can act then."&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Monday, 15th of January 2024, 08.56 AM&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;Client&lt;/strong&gt;: "How about the metrics of our application? A week has gone by already. Can you see something that can be improved? The users are losing their patience, and I am starting to get afraid. Yesterday, I found the windows of my car broken, with a note inside: my users request response times of less than 1 second. Otherwise, things will become more serious, they wrote."&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Monday, 15th of January 2024, 10.25 AM&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;Developer&lt;/strong&gt;: "It is a reasonable request, I must say. Any website nowadays should have a response time of less than 1 second on average. Maybe you can consider it a turning point in your life and stop using a car? It is healthier to walk, you will feel better. But I see there are two endpoints that take up to 50% of the load of the system. Both are queries, i.e., they just get data to show it to the user. They heavily use Doctrine, an ORM, so we are facing the famous N+1 problem. But fear not! I will fight back and win: we should be able to cache the data in Redis, so we avoid querying the database every time to get the same data, and get the calculated data straight from memory. Just hang in there."&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Friday, 19th of January 2024, 02.05 AM&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;Client&lt;/strong&gt;: "Hey! How is that performance improvement going? My family and I are «spending away time» with some of the &lt;del&gt;angriest&lt;/del&gt; &lt;del&gt;craziest&lt;/del&gt; users of our application. The worst? They keep showing me how slow and unusable it is. I have come to the point where I understand them. Anyway, let me know, how the improvement is going."&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Friday, 19th of January 2024, 12.46 PM&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;Developer&lt;/strong&gt;: "I am glad to know that you are enjoying some time off. You deserve it, buddy! In the meantime, I optimized both endpoints that were the slowest: they are now using projections, and all the models they require are cached in Redis. However, I cannot see much improvement, the response times are still over 1 second. Well, it is Friday noon, so I will look into it first thing on Monday. Have a nice weekend!"&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Saturday, 20th of January 2024, 03.23 AM&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;Client&lt;/strong&gt;: "HEY MAN! THEY TOOK MY DAUGHTER AWAY. THEY MADE ME SAY GOOD BYE TO HER. I DO NOT KNOW WHAT IS HAPPENING ANYMORE."&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Monday, 22nd of January 2024, 12.28 PM&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;Developer&lt;/strong&gt;: "They grow up so fast! It is good to let them have space and make their own mistakes. Well, I found it: it seems we forgot to enable OPcache. I enabled it at 11.45 AM. Look how we reduced the response times:"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fftxd7y49xgh27n9kzbq4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fftxd7y49xgh27n9kzbq4.png" alt="Response times"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;"Here is the chart with transactons time after enabling OPcache:"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5zeftv209ki9e6487umh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5zeftv209ki9e6487umh.png" alt="Transactions time"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;"And here is the chart with the AppIndex score:"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnddi059obzaqti8t4ans.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnddi059obzaqti8t4ans.png" alt="AppIndex score"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;"Problem solved!"&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Monday, 22nd of January 2024, 12.29 PM&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;Client&lt;/strong&gt;: "WTF IS AN OPCACHE?"&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Monday, 22nd of January 2024, 3.07 PM&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;Developer&lt;/strong&gt;: "PHP is an interpreted language. That means every time a script is executed in a request, the code must be converted to bytecode (or machine code), i.e., the code the computer understands. However, in production, the code is always the same amongst each deploy, so that conversion can be cached for all requests. That is what OPcache does. By the way, you should look into your «Caps. lock» key. It seems stuck."&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Monday, 29th of January 2024, 11.50 AM&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;Developer&lt;/strong&gt;: "Here is the bill. You could have at least thanked me after all the work I put when optimizing your application."&lt;/p&gt;

</description>
      <category>php</category>
      <category>performance</category>
      <category>cache</category>
      <category>opcache</category>
    </item>
    <item>
      <title>Weekly reading digest #27</title>
      <dc:creator>Rubén Rubio</dc:creator>
      <pubDate>Sun, 18 Feb 2024 09:00:00 +0000</pubDate>
      <link>https://dev.to/rubenrubiob/weekly-reading-digest-27-4kcm</link>
      <guid>https://dev.to/rubenrubiob/weekly-reading-digest-27-4kcm</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/hbgl/elegant-immutable-object-pattern-in-php-1dg3"&gt;Elegant immutable object pattern in PHP&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://davorminchorov.com/articles/building-maintainable-php-applications-thinking-data-vs-thinking-business-processes" rel="noopener noreferrer"&gt;Building Maintainable PHP Applications: Thinking Data vs Thinking Business Processes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.jetbrains.com/phpstorm/2024/02/ai-for-php-how-to-automate-unit-testing-using-ai-assistant/" rel="noopener noreferrer"&gt;AI for PHP: How To Automate Unit Testing Using AI Assistant?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/mainick/dto-vs-vo-in-php-4adi"&gt;DTO vs VO in PHP&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@vlreshet/stop-using-old-fashioned-closures-in-modern-php-there-are-4-ways-to-replace-them-51d8661e2f7e" rel="noopener noreferrer"&gt;Stop using old-fashioned closures in modern PHP. There are 4* ways to replace them.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackoverflow.com/a/21903119" rel="noopener noreferrer"&gt;How to get URL parameter using jQuery or plain JavaScript?&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://sebastiandedeyne.com/how-i-take-notes-structure-with-now-next-notes" rel="noopener noreferrer"&gt;How I take notes: Structure with Now Next Notes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://fortelabs.com/blog/para/" rel="noopener noreferrer"&gt;The PARA Method: The Simple System for Organizing Your Digital Life in Seconds&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://adamgrant.substack.com/p/beyond-toxic-positivity" rel="noopener noreferrer"&gt;Beyond toxic positivity&lt;/a&gt;&lt;/p&gt;

</description>
      <category>backend</category>
      <category>php</category>
      <category>symfony</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Weekly reading digest #26</title>
      <dc:creator>Rubén Rubio</dc:creator>
      <pubDate>Sun, 11 Feb 2024 09:00:00 +0000</pubDate>
      <link>https://dev.to/rubenrubiob/weekly-reading-digest-25-2cm</link>
      <guid>https://dev.to/rubenrubiob/weekly-reading-digest-25-2cm</guid>
      <description>&lt;p&gt;&lt;a href="https://jesusvalerareales.com/three-dots-in-php/" rel="noopener noreferrer"&gt;Three dots in PHP. First-Class Callable Syntax, Variadic function &amp;amp; Argument unpacking&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.exakat.io/en/common-services-with-php-application/" rel="noopener noreferrer"&gt;Common services with PHP applications&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.oussama-mater.tech/code-to-an-interface/" rel="noopener noreferrer"&gt;Code to an interface!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://compiler.blog/understanding-automated-testing" rel="noopener noreferrer"&gt;Understanding Automated Testing&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dbtoolsbundle.readthedocs.io/en/stable/" rel="noopener noreferrer"&gt;DbToolsBundle. A set of Symfony Console Commands to interact with your database&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://symfonystation.mobileatom.net/symfony-ux-stimulus-js-ui" rel="noopener noreferrer"&gt;Discover Symfony UX. UI with Stimulus = fewer JS headaches for you&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/beyn-technology/wars-of-symfony-runtimes-a-performance-odyssey-7b0120e8f9e1" rel="noopener noreferrer"&gt;Wars of Symfony Runtimes: A Performance Odyssey&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.packagist.com/composer-2-7-and-cve-2024-24821/" rel="noopener noreferrer"&gt;Composer 2.7 and CVE-2024-24821: Code execution and possible privilege escalation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.blog/2022-05-09-supercharging-github-actions-with-job-summaries/" rel="noopener noreferrer"&gt;Supercharging GitHub Actions with Job Summaries&lt;/a&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>symfony</category>
      <category>backend</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Weekly reading digest #25</title>
      <dc:creator>Rubén Rubio</dc:creator>
      <pubDate>Sun, 04 Feb 2024 09:00:00 +0000</pubDate>
      <link>https://dev.to/rubenrubiob/weekly-reading-digest-25-4nj5</link>
      <guid>https://dev.to/rubenrubiob/weekly-reading-digest-25-4nj5</guid>
      <description>&lt;p&gt;&lt;a href="https://chriswhite.is/coding/generating-large-zip-exports-from-files-in-s3/" rel="noopener noreferrer"&gt;Generating Large ZIP Exports From Files in S3&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://danielrotter.at/2023/09/22/avoid-mocking-repositories-by-using-in-memory-implementations.html" rel="noopener noreferrer"&gt;Avoid mocking repositories by using in-memory implementations&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://lukeplant.me.uk/blog/posts/yagni-exceptions/" rel="noopener noreferrer"&gt;YAGNI exceptions&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://simonwillison.net/2021/Jul/1/pagnis/" rel="noopener noreferrer"&gt;PAGNIs: Probably Are Gonna Need Its&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.sarahmei.com/blog/2013/11/11/why-you-should-never-use-mongodb/" rel="noopener noreferrer"&gt;Why You Should Never Use MongoDB&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tom.preston-werner.com/2010/08/23/readme-driven-development.html" rel="noopener noreferrer"&gt;Readme Driven Development&lt;/a&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>symfony</category>
      <category>backend</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Weekly reading digest #24</title>
      <dc:creator>Rubén Rubio</dc:creator>
      <pubDate>Sun, 28 Jan 2024 09:00:00 +0000</pubDate>
      <link>https://dev.to/rubenrubiob/weekly-reading-digest-24-1ka1</link>
      <guid>https://dev.to/rubenrubiob/weekly-reading-digest-24-1ka1</guid>
      <description>&lt;p&gt;&lt;a href="https://www.exakat.io/en/all-the-recursive-functions-in-php/" rel="noopener noreferrer"&gt;All the recursive Functions in PHP&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://php.watch/articles/composer-gitattributes" rel="noopener noreferrer"&gt;GitAttributes for PHP Composer Projects&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://doeken.org/blog/repository-pattern" rel="noopener noreferrer"&gt;The infamous Repository Pattern in PHP&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/@mounir.mouih/designing-fine-grained-independent-features-using-symfony-and-strategy-pattern-084a0740fa5d" rel="noopener noreferrer"&gt;Designing fine-grained independent features using Symfony and Strategy Pattern&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.theodo.com/2023/10/ensure-that-symfony-routes-have-access-control/" rel="noopener noreferrer"&gt;How to ensure that all the routes on my Symfony app have access control&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://alexcabal.com/posts/standard-ebooks-and-classic-web-tech" rel="noopener noreferrer"&gt;How Standard Ebooks serves millions of requests per month with a 2gb vps; or, a paean to the classic web&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://testing-tips.sarvendev.com/#/" rel="noopener noreferrer"&gt;Testing tips&lt;/a&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>backend</category>
      <category>symfony</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Weekly reading digest #23</title>
      <dc:creator>Rubén Rubio</dc:creator>
      <pubDate>Sun, 21 Jan 2024 09:00:00 +0000</pubDate>
      <link>https://dev.to/rubenrubiob/weekly-reading-digest-23-hdd</link>
      <guid>https://dev.to/rubenrubiob/weekly-reading-digest-23-hdd</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/dimdev/performance-benchmark-of-php-runtimes-2lmc"&gt;Performance benchmark of PHP runtimes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dkarlovi.github.io/symfony-feature-flags/" rel="noopener noreferrer"&gt;Adding feature flags to your Symfony app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.oracle.com/cd/E11882_01/server.112/e41573/technique.htm#PFGRF032" rel="noopener noreferrer"&gt;Performance Improvement Methods&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://not-a-number.io/2024/understanding-kolmogorov-complexity/" rel="noopener noreferrer"&gt;Understanding Kolmogorov Complexity&lt;/a&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>symfony</category>
      <category>backend</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Weekly reading digest #22</title>
      <dc:creator>Rubén Rubio</dc:creator>
      <pubDate>Sun, 14 Jan 2024 09:00:00 +0000</pubDate>
      <link>https://dev.to/rubenrubiob/weekly-reading-digest-22-184h</link>
      <guid>https://dev.to/rubenrubiob/weekly-reading-digest-22-184h</guid>
      <description>&lt;p&gt;&lt;a href="https://frankenphp.dev/docs/production/" rel="noopener noreferrer"&gt;Deploying in Production | FrankenPHP&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://sebastiandedeyne.com/introducing-tabular-assertions" rel="noopener noreferrer"&gt;Introducing tabular assertions&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://sebastiandedeyne.com/tabular-assertions-with-phpunit" rel="noopener noreferrer"&gt;Tabular assertions with PHPUnit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://davorminchorov.com/articles/building-maintainable-php-applications-framework-decoupling-vs-framework-coupling" rel="noopener noreferrer"&gt;Building Maintainable PHP Applications: Framework Decoupling vs Framework Coupling&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://thecodingmachine.io/solving-n-plus-1-problem-in-orms" rel="noopener noreferrer"&gt;Solving the N+1 problem in ORMs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/pXVZhFE2pbk" rel="noopener noreferrer"&gt;What is a Command Bus? (vídeo)&lt;/a&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>symfony</category>
      <category>backend</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Optimising a web application (III): projecting</title>
      <dc:creator>Rubén Rubio</dc:creator>
      <pubDate>Wed, 10 Jan 2024 09:00:00 +0000</pubDate>
      <link>https://dev.to/rubenrubiob/optimising-a-web-application-iii-projecting-1ij3</link>
      <guid>https://dev.to/rubenrubiob/optimising-a-web-application-iii-projecting-1ij3</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;With this post, we finish the series of optimising a web application. As a reminder: the application consists of a Symfony application to manage the entrance and exit to buildings using validation of previously purchased tickets.&lt;/p&gt;

&lt;p&gt;In the previous post, we performed some optimisations in an application by indexing the database. We improved the performance by a 57 %. However, the absolute response times were of around 300 ms, that we consider too high. In this post, we will investigate further issues and solve them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue
&lt;/h2&gt;

&lt;p&gt;As in the previous case, we relied on New Relic to identify the problem. After reviewing both the endpoints and the database metrics, we found the queries that were causing the issue. The application provides some real-time data for its users: the number of people inside a building and the names of the people inside a building (only for administrators). To provide this information, the application uses tables with logs of all the check-ins and check-outs of users.&lt;/p&gt;

&lt;p&gt;So, in order to calculate the number of people there are now inside a building, the application counts the total of check-ins and checkouts along its whole existence, i.e., it scans data from years ago to give the numbers of the present moment. The query it uses is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_checkin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;event_checkin&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_checkin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;building_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;building_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_checkin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_on&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;(:&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="cm"&gt;/*...*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The query is quite complex and used in several paths, affecting business rules, so we decided not to change it and look for alternatives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;We want to improve the performance for end-users and administrators, as they are using the application to check in/checkout: they need it to be fast to access/leave the building. The rest of paths are not critical in terms of response time.&lt;/p&gt;

&lt;p&gt;We actually only want two pieces of data for these critical paths:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The number of people inside the building.&lt;/li&gt;
&lt;li&gt;The names and some extra data, like the email, of the people inside the building (only available for the administrators).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What we can do is have this data prepared: we can act when the user checks-in and checks-out. We will use events to denormalize the data we need and generate projections: we will save together all data we need, so we only have to perform simple queries to get it.&lt;/p&gt;

&lt;p&gt;The schema for the check-in will be like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0l_DAtIE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nw95sdyl1qmcswv7yqzl.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0l_DAtIE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nw95sdyl1qmcswv7yqzl.jpeg" alt="Check-in/Checkout schema" width="800" height="581"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When a user checks in, an event is fired to a queue service. Afterwards, two consumers process the event and execute the required action: updating the number of people inside a building or generating a projection with the user data. The checkout case is analogous.&lt;/p&gt;

&lt;p&gt;We will only perform the changes to these paths, and leave the rest of paths untouched, so we do not affect the domain logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;The application uses hexagonal architecture, so these two commands, for check-in and checkout, are isolated and well identified in code. We had to perform the following changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fire the events from the domain entities when a ticket is checked-in/checked-out.&lt;/li&gt;
&lt;li&gt;Consume the events and generate/update the projections:

&lt;ul&gt;
&lt;li&gt;Update the number of people inside a building, by increasing or decreasing a value, depending on if the command is a check-in or a checkout, respectively.&lt;/li&gt;
&lt;li&gt;Insert or delete a row with the data of the use inside a building, depending on if the command is a check-in or a checkout, respectively.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Change the paths from the external layer (the controllers) to the domain layer, so it reads the projected data instead of querying the database.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We could use Redis or another persistence system, but we chose to use MySQL to avoid performing more changes to the project. As the project uses hexagonal architecture, it is easy to swap the implementation in the future if required. We used Symfony Messenger with RabbitMQ as broker for messaging. How we set it up is beyond the scope of this post.&lt;/p&gt;

&lt;p&gt;In the end, the queries we had to perform were simple and fast. To get the data of the people inside a building, the query becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;total_in&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;view_building_data&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;building_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;building_id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the query to get the information of people inside a building:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;view_users_inside_building&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;building_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;building_id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both queries are using primary keys, so they are optimal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;Unfortunately, the application is seasonal, i.e., it is only used in Summer, and we deployed these changes in Autumn. Therefore, we can not validate the results in production until next Summer.&lt;/p&gt;

&lt;p&gt;We did, nonetheless, some checks ourselves. Both endpoints' response time was of around 1.1 seconds (with the possibility to get higher as the data grows up). After deploying the changes, the response time decreased to 150ms. As both queries are simple and use primary indexes, we are sure that this response time will always be similar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;We can allegedly affirm that we improved the response time. We will know for sure next Summer (there will be a post about it).&lt;/p&gt;

&lt;p&gt;As for future improvements, we could stop using the logs tables everywhere they are used, and analyse that data differently, instead. It will be done if needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Again, using New Relic we identified performance issues in the queries of the application.&lt;/li&gt;
&lt;li&gt;We proposed a solution based on events to decouple the application and project the data we need.&lt;/li&gt;
&lt;li&gt;We implemented that solution by only changes those paths of the application that are most important for end-users.&lt;/li&gt;
&lt;li&gt;We could not assert that the changes work in production, as the application is seasonal.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>php</category>
      <category>symfony</category>
      <category>backend</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
