<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Blogs on Lukasz Zagroba</title>
    <link>https://lzag.dev/blog/</link>
    <description>Recent content in Blogs on Lukasz Zagroba</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Sat, 18 Apr 2026 09:00:00 +0000</lastBuildDate><atom:link href="https://lzag.dev/blog/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Building Tickstream: Real-Time Crypto Analytics with an AI Assist</title>
      <link>https://lzag.dev/tickstream/</link>
      <pubDate>Sat, 18 Apr 2026 09:00:00 +0000</pubDate>
      <guid>https://lzag.dev/tickstream/</guid>
      <description>&lt;p&gt;I built &lt;a href=&#34;https://tickstream.lzag.dev&#34;&gt;Tickstream&lt;/a&gt; over a few weekends. It&amp;rsquo;s a real-time crypto analytics pipeline: BTC and ETH prices stream in from Coinbase, anomalies get detected against a rolling baseline, a local LLM generates narrative for significant moves, and news events get correlated with price reactions after the fact. Everything is observable through a Grafana dashboard embedded in a landing page.&lt;/p&gt;
&lt;p&gt;This post is a walkthrough of how the system works and an honest account of building it with AI assistance.&lt;/p&gt;</description>
      <content>&lt;p&gt;I built &lt;a href=&#34;https://tickstream.lzag.dev&#34;&gt;Tickstream&lt;/a&gt; over a few weekends. It&amp;rsquo;s a real-time crypto analytics pipeline: BTC and ETH prices stream in from Coinbase, anomalies get detected against a rolling baseline, a local LLM generates narrative for significant moves, and news events get correlated with price reactions after the fact. Everything is observable through a Grafana dashboard embedded in a landing page.&lt;/p&gt;
&lt;p&gt;This post is a walkthrough of how the system works and an honest account of building it with AI assistance.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;the-architecture&#34;&gt;The Architecture&lt;/h2&gt;
&lt;p&gt;The system is a set of independent services, each responsible for one concern, communicating through an in-process event bus. This keeps the design modular: each piece can fail, retry, or be replaced without cascading through the rest.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt; Coinbase WebSocket
        │
        ▼
  Price Consumer Verticle ─────────────────────┐
        │                                      │
        ▼                                      ▼
 Anomaly Detection Verticle            InfluxDB Verticle
        │                                      ▲
        ├──► Push Notification (ntfy)          │
        │                                      │
        └──► InfluxDB (anomaly regions) ───────┘

 Finnhub News ──► Sentiment (Ollama) ──► Price Impact ──► InfluxDB (events)
                                                |
                                                ▼
                                          SQLite storage
                                                │
                                                ▼
                                         Daily Briefing (ntfy)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Prices stream in from Coinbase&amp;rsquo;s WebSocket feed and flow in two directions: straight to storage, and through an anomaly detector that watches for significant moves against a rolling baseline. When something crosses the threshold, a push notification goes out and the anomaly is recorded alongside the price data.&lt;/p&gt;
&lt;p&gt;News events are handled separately. A scheduler polls Finnhub at regular intervals, and each new item gets run through a local LLM for sentiment analysis before being matched against the price data in the surrounding window. The result is a record of what happened in the market alongside what was being said about it at the time — the correlation is more interesting than either signal alone.&lt;/p&gt;
&lt;p&gt;Everything is observable through Grafana: price history, detected anomalies, annotated news events, and application metrics and logs. The whole thing runs in Docker Compose on a Hetzner CX32, with Caddy as the reverse proxy handling the landing page and Grafana panel embedding.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;what-i-learned&#34;&gt;What I Learned&lt;/h2&gt;
&lt;p&gt;Vert.x is well suited to event-driven processing — the verticle model keeps concerns clean and the event loop handles high-frequency ticks without drama. The harder part was making it all stable: getting flows to restart on failure, wiring up retries with the right backoff, and making sure the observability was good enough to actually know the app was working as it should. Health checks, Prometheus metrics, and structured logs help keep an eye on the status of the app.&lt;/p&gt;
&lt;p&gt;InfluxDB 3 was a deliberate choice to work with a timeseries database outside my usual stack. Grafana was the highlight. The flexibility of the dashboard builder — composable layouts, annotation overlays, variable-driven queries, Loki integration alongside metrics — is something I hadn&amp;rsquo;t fully appreciated before. The fact that it&amp;rsquo;s open source and self-hosted makes it even better.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;ai-assisted-development-what-it-actually-looked-like&#34;&gt;AI-Assisted Development: What It Actually Looked Like&lt;/h2&gt;
&lt;p&gt;I used Claude Code throughout — it runs in the terminal, integrates naturally with Vim and my Omarchy setup, and keeps context on the full codebase across the session.&lt;/p&gt;
&lt;p&gt;The best use was kick-starting unfamiliar territory. InfluxDB 3 and the Grafana dashboard format were both fairly new to me, and having a working starting point — even an imperfect one — was faster than reading the full API surface from scratch. The generated code for InfluxDB queries gave me something to run against and iterate on. Same with Grafana: the dashboard JSON was based on older schema versions and needed corrections, but it was a better starting point than a blank file. The problem is that the model&amp;rsquo;s training data skews toward older documentation and Stack Overflow answers. For anything touching a recently-changed API or a niche tool, going straight to the official docs is faster than iterating through confident wrong answers. The one frustrating limitation: Claude won&amp;rsquo;t give you direct links. Being pointed to the right documentation page would have saved real time on multiple occasions.&lt;/p&gt;
&lt;p&gt;The bigger lesson was about architecture. I generated a first version quickly and then realised most of the structural decisions were wrong. Vibe coding doesn&amp;rsquo;t work — not because the generated code is bad line by line, but because you can produce plausible-looking architecture that solves the wrong problem. The anomaly detector was the clearest example: the first generated version looked reasonable, but when I read it carefully it didn&amp;rsquo;t actually match what I needed. The issue was that I hadn&amp;rsquo;t thought through what anomaly detection should mean for this feed — what constitutes an entry, an exit, where to anchor the baseline. Only after working that out myself was I able to describe the problem clearly enough to get good code. The model can implement a solution well; it can&amp;rsquo;t figure out what the solution should be.&lt;/p&gt;
&lt;p&gt;This also puts new pressure on code review. It&amp;rsquo;s surprisingly easy to generate nonsense that looks correct, compiles, and passes tests — especially when the tests were also written by AI. The test suite validates the implementation&amp;rsquo;s own assumptions, not yours. Reading AI-generated code requires more attention than reading code a colleague wrote, not less — because a colleague usually has a mental model of the problem that leaks into the code in legible ways. Generated code can be locally coherent but globally wrong, and the only way to catch that is to actually understand what it&amp;rsquo;s doing.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;working-with-local-llms&#34;&gt;Working with Local LLMs&lt;/h2&gt;
&lt;p&gt;Running Ollama locally is useful for a side project — no API costs, no data leaving the machine, and baking the model into the Docker image at build time means the container starts with no runtime dependency on a model registry. The harder problem is getting consistent, meaningful output. Out of the box, llama3.2:3b was pretty good at generating the daily briefing based on the news from the day, but when doing sentiment analysis it was skewing bearish on almost everything — I think that&amp;rsquo;s an inherent bias in the base model toward negative framing in financial contexts. I fine-tuned it to correct that, and overcorrected: it started reading bullish into news that clearly wasn&amp;rsquo;t. After another round it&amp;rsquo;s drifted bearish again. The scores are still inconsistent in a way that&amp;rsquo;s hard to reason about. Fine-tuning a small model for a specific analytical task is not as straightforward as it sounds, and I&amp;rsquo;m not convinced I&amp;rsquo;ve found the right approach yet. It&amp;rsquo;s something I want to dig into more seriously — the question of how to reliably adapt a general-purpose model to a narrow domain is genuinely interesting and I&amp;rsquo;ve only scratched the surface.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;the-project-as-a-portfolio-artifact&#34;&gt;The Project as a Portfolio Artifact&lt;/h2&gt;
&lt;p&gt;The goal from the start was to demonstrate Kotlin stream processing in a real setting. The AI assistance made it faster to build, and occasionally introduced errors that I had to catch and fix. That&amp;rsquo;s about the right way to describe it. The judgment about what to build, whether it was working correctly, and where the design was going wrong — that stayed with me. Which is, I think, exactly where it should be.&lt;/p&gt;
&lt;p&gt;The project page is at &lt;a href=&#34;https://tickstream.lzag.dev&#34;&gt;tickstream.lzag.dev&lt;/a&gt;.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>AI Tug of War: Everyone&#39;s Shipping. Nobody&#39;s Building.</title>
      <link>https://lzag.dev/ai-tug-of-war/</link>
      <pubDate>Mon, 23 Mar 2026 09:00:00 +0000</pubDate>
      <guid>https://lzag.dev/ai-tug-of-war/</guid>
      <description>&lt;p&gt;Everyone is offloading the hard parts. The pipeline is clogged. And the generation that was supposed to learn — didn&amp;rsquo;t.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;There is a new dynamic spreading through engineering teams, and almost nobody is naming it clearly. It isn&amp;rsquo;t about productivity. It&amp;rsquo;s about offloading. Specifically, it&amp;rsquo;s about who can use AI to generate work that forces someone else to do the hardest part.&lt;/p&gt;
&lt;p&gt;Junior developers can now produce an extraordinary volume of code. Pull requests come in at a pace that would have been unthinkable two years ago. The problem is that generating a PR and writing correct, reviewable code are not the same thing. Seniors are being buried under a growing mountain of AI-generated output that looks plausible on the surface but requires genuine expertise to evaluate. The pipeline isn&amp;rsquo;t moving faster — it&amp;rsquo;s clogging.&lt;/p&gt;</description>
      <content>&lt;p&gt;Everyone is offloading the hard parts. The pipeline is clogged. And the generation that was supposed to learn — didn&amp;rsquo;t.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;There is a new dynamic spreading through engineering teams, and almost nobody is naming it clearly. It isn&amp;rsquo;t about productivity. It&amp;rsquo;s about offloading. Specifically, it&amp;rsquo;s about who can use AI to generate work that forces someone else to do the hardest part.&lt;/p&gt;
&lt;p&gt;Junior developers can now produce an extraordinary volume of code. Pull requests come in at a pace that would have been unthinkable two years ago. The problem is that generating a PR and writing correct, reviewable code are not the same thing. Seniors are being buried under a growing mountain of AI-generated output that looks plausible on the surface but requires genuine expertise to evaluate. The pipeline isn&amp;rsquo;t moving faster — it&amp;rsquo;s clogging.&lt;/p&gt;
&lt;h2 id=&#34;it-runs-both-ways&#34;&gt;It Runs Both Ways&lt;/h2&gt;
&lt;p&gt;But let&amp;rsquo;s not pretend this is only a junior problem. Senior engineers who want to ship fast are doing the same thing in the other direction — using AI to crank out features and, when something inevitably breaks, deferring the investigation and cleanup to junior teammates. Meanwhile, managers can now generate entire sprint backlogs from a single brainstorm session. Tickets appear at volume, each one requiring someone lower in the chain to actually determine whether the idea is technically coherent. Nobody is accountable for the mess they&amp;rsquo;ve created upstream, because generating the mess required almost no effort.&lt;/p&gt;
&lt;p&gt;This is the tug of war: every role has found a way to use AI to pass the genuinely difficult work to someone else. The result isn&amp;rsquo;t more velocity. It&amp;rsquo;s more noise at every layer of the organization, with the same or worse actual delivery — and, when something ships, the quality often suffers visibly.&lt;/p&gt;
&lt;h2 id=&#34;the-same-disease-in-your-feed&#34;&gt;The Same Disease in Your Feed&lt;/h2&gt;
&lt;p&gt;This pattern isn&amp;rsquo;t limited to code. Look at any professional content feed right now. The volume of long-form posts has exploded because turning a half-baked idea into a thousand words takes about ninety seconds with the right prompt. The posts look considered. They use all the right frameworks. But scroll through enough of them and something becomes clear: almost nothing new is actually being said. It&amp;rsquo;s the same ideas, recirculated, reworded, re-packaged. The marginal cost of producing content has collapsed to near zero, and so the feed has become a landfill of well-formatted emptiness.&lt;/p&gt;
&lt;h2 id=&#34;fake-productivity&#34;&gt;Fake Productivity&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Funny how there&amp;rsquo;s so much productivity, but no product.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We are now at the point where it is becoming possible to see clearly what the AI productivity boom actually was — at least in part. A lot of it was fake. Not fake in the sense that nothing got built. Things got built. But the output wasn&amp;rsquo;t meaningfully better, the code wasn&amp;rsquo;t meaningfully more correct, and the ideas weren&amp;rsquo;t meaningfully more original. What grew was volume. Volume of PRs, volume of tickets, volume of posts, volume of job applications. Noise, not signal.&lt;/p&gt;
&lt;p&gt;The tools for filtering that noise have not kept pace. Code review cannot be fully automated in any way that provides real safety guarantees. Human judgment is still the bottleneck, and human reviewers are being asked to process an order of magnitude more input than they were before, with roughly the same number of hours in the day. Something has to give — and what gives is either quality or people&amp;rsquo;s sanity.&lt;/p&gt;
&lt;h2 id=&#34;the-generation-that-didnt-learn&#34;&gt;The Generation That Didn&amp;rsquo;t Learn&lt;/h2&gt;
&lt;p&gt;There is a longer-horizon problem that deserves more attention than it&amp;rsquo;s getting. Engineering skill is built through friction. You learn a technology by fighting with it — by hitting the wall, reading the stack trace, understanding why the abstraction leaked, internalizing the behavior. That process is slow and sometimes painful, and it is also irreplaceable.&lt;/p&gt;
&lt;p&gt;A significant portion of the junior developers entering the field right now are not going through that process. They are going through a different one: write a prompt, get code, debug it with another prompt, ship it. This can produce results fast enough to pass code review in many organizations. It does not produce engineers who understand what they built.&lt;/p&gt;
&lt;p&gt;The consequences of that are deferred, not avoided. Systems will break. Incidents will happen. When they do, you need engineers who can reason about what the system is actually doing — not just engineers who can prompt their way toward a solution in ideal conditions. The bill for skipping the hard work of learning is coming. It is just not due today.&lt;/p&gt;
&lt;h2 id=&#34;where-this-leaves-us&#34;&gt;Where This Leaves Us&lt;/h2&gt;
&lt;p&gt;The organizations that figure this out fastest will be the ones that raise the bar on what counts as a reviewable contribution — not to be obstructionist, but because high standards are genuinely the only defense against a pipeline flooded with plausible-looking slop. That means investing in tooling, yes, but more fundamentally, it means rebuilding a culture that takes correctness seriously as a shared responsibility rather than something to be deferred upstream or downstream.&lt;/p&gt;
&lt;p&gt;Individually, the question is simpler but harder to answer honestly: where do you actually stand? Are you the one generating the noise, or the one with the judgment to evaluate it? Because in a world where content and code can be produced at near-zero cost, the scarce thing — the actually valuable thing — is the &lt;a href=&#34;https://lzag.dev/judgement-in-software-development/&#34;&gt;judgment to know what is good&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;That judgment doesn&amp;rsquo;t come from prompts. It still comes from doing the work.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Exploring APT</title>
      <link>https://lzag.dev/exploring-apt/</link>
      <pubDate>Sat, 06 Sep 2025 23:38:06 +0000</pubDate>
      <guid>https://lzag.dev/exploring-apt/</guid>
      <description>&lt;p&gt;As a software developer, I like to really understand the tools I use every day to feel more in control and aware. I’ve been using APT for a long time to handle updates, dependencies, and installations on my Debian-based systems, but I’ve never looked into how it actually works. Learning about things like how it uses GPG keys to check repositories or manages software sources could come in handy for tasks like setting up Docker containers or solving issues with software updates.&lt;/p&gt;</description>
      <content>&lt;p&gt;As a software developer, I like to really understand the tools I use every day to feel more in control and aware. I’ve been using APT for a long time to handle updates, dependencies, and installations on my Debian-based systems, but I’ve never looked into how it actually works. Learning about things like how it uses GPG keys to check repositories or manages software sources could come in handy for tasks like setting up Docker containers or solving issues with software updates.&lt;/p&gt;
&lt;h4 id=&#34;intro&#34;&gt;intro&lt;/h4&gt;
&lt;p&gt;APT (Advanced Package Tool) is a powerful, high-level package management system used by Debian-based Linux distributions like Ubuntu. It&amp;rsquo;s a suite of tools, including apt-get and apt-cache, that simplifies software management by relying on the lower-level dpkg tool to install, configure, and remove software packages.&lt;/p&gt;
&lt;p&gt;At the heart of Debian-based systems like Ubuntu are .deb packages, which function as software installers. These packages are archives containing two main parts: the application&amp;rsquo;s files, laid out in the exact directory structure they&amp;rsquo;ll be installed in, and a DEBIAN directory holding crucial metadata. This metadata, particularly in the control file, lists the package&amp;rsquo;s version, description, and dependencies. This is where the apt package manager comes in; it reads this information to automatically handle installations, upgrades, and the complex web of dependencies, ensuring that all necessary software components are in place for a smooth and functional system.&lt;/p&gt;
&lt;p&gt;Applications rarely exist in isolation and often depend on other programs or libraries to function correctly. Manually tracking and installing every single dependency would be a nightmare and this is where APT comes in. When you ask APT to install a package, it intelligently reads the package&amp;rsquo;s metadata, identifies all its dependencies, and automatically installs them from its configured sources.&lt;/p&gt;
&lt;h4 id=&#34;configuration&#34;&gt;configuration&lt;/h4&gt;
&lt;p&gt;So, where does APT get all this software? From repositories. A repository is essentially a remote server (or even a local directory) that hosts a collection of .deb packages and crucial metadata files. Your system knows which repositories to check by reading configuration files. The primary file is /etc/apt/sources.list, and additional repository sources are placed as separate .list files in the /etc/apt/sources.list.d/ directory. You can view your configured sources using the cat command on these files.&lt;/p&gt;
&lt;p&gt;Repositories contain index files (like Packages.gz), which act as a catalog for all the available software. When you run the sudo apt update command, APT downloads the latest versions of these index files from all configured repositories. It then builds a local cache of available packages, their versions, and their dependencies on your machine.&lt;/p&gt;
&lt;p&gt;When you later run a command like &lt;code&gt;sudo apt install &amp;lt;package-name&amp;gt;&lt;/code&gt;, APT consults this local cache to find the package and figure out exactly what needs to be downloaded and installed to make it work.&lt;/p&gt;
&lt;h4 id=&#34;gpg-authentication&#34;&gt;gpg authentication&lt;/h4&gt;
&lt;p&gt;Using GPG with APT ensures the security and authenticity of software packages. GPG keys are used to verify the digital signatures of a repository&amp;rsquo;s metadata and packages, confirming they haven&amp;rsquo;t been tampered with and come from a trusted source. Repository maintainers sign their Release files with a private GPG key. When you run apt update, APT downloads this signed file and uses the corresponding public GPG key (which you&amp;rsquo;ve trusted and added to your system) to validate the signature. This establishes a chain of trust, protecting against malicious packages by ensuring that the software you&amp;rsquo;re about to install is exactly what the repository intended for you.&lt;/p&gt;
&lt;h4 id=&#34;components-and-suites&#34;&gt;components and suites&lt;/h4&gt;
&lt;p&gt;When you look at a repository entry in your sources.list file, you&amp;rsquo;ll see terms like main, universe, stable, or focal. These are components and suites.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Components&lt;/strong&gt;: These are categories of software within a repository, usually grouped by licensing or support level. For example, Ubuntu uses main (fully supported), universe (community-maintained), restricted (proprietary drivers), and multiverse (software with legal restrictions). Most third-party repositories will simply use a main component.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Suites&lt;/strong&gt;: Suites: These refer to a specific release of your operating system, telling APT which set of packages matches your system. For example, if you’re using Ubuntu 22.04, the suite is jammy. Other sources might use suites like stable or nightly to signal which version of the software you want to track.&lt;/p&gt;
&lt;h4 id=&#34;source-file-syntax&#34;&gt;source file syntax&lt;/h4&gt;
&lt;p&gt;APT source files have two main syntaxes: the traditional one-line format and the newer deb822 format. The one-line format is a single entry that&amp;rsquo;s easy to read, while the deb822 format is a more structured, multi-line format with distinct fields. The most comprehensive documentation can be found in the sources.list man page.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;One-line Format&lt;/strong&gt;&lt;br&gt;
This is the classic syntax where each repository is on a single line. It&amp;rsquo;s often found in .list files.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;deb http://us.archive.ubuntu.com/ubuntu/ jammy main restricted
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;deb822 Format&lt;/strong&gt;&lt;br&gt;
This is a newer, more readable syntax that uses a stanza-based structure, similar to a .deb package control file. It&amp;rsquo;s often used in .sources files in the sources.list.d/ directory. In this format, each piece of information has a clear label.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Types: deb
URIs: http://us.archive.ubuntu.com/ubuntu/
Suites: jammy
Components: main restricted
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h4 id=&#34;useful-commands&#34;&gt;useful commands&lt;/h4&gt;
&lt;p&gt;Below I&amp;rsquo;m listing the most useful commands with short descriptions for reference.&lt;/p&gt;
&lt;h4 id=&#34;apt-update&#34;&gt;apt update&lt;/h4&gt;
&lt;p&gt;Refreshes the local package cache. This command retrieves the latest index files from all configured repositories but does not install or upgrade any packages. It&amp;rsquo;s a critical first step to ensure your system knows what software versions are available.&lt;/p&gt;
&lt;h4 id=&#34;apt-search&#34;&gt;apt search&lt;/h4&gt;
&lt;p&gt;The apt search command is a powerful tool for discovering new software, not just checking for a given name. It searches the local package cache for packages and descriptions that match a given name or keyword. This is useful when you&amp;rsquo;re looking for a specific type of software but don&amp;rsquo;t know the exact package name.&lt;/p&gt;
&lt;p&gt;For example, if you wanted to find different web servers available in your repositories, you could run:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt search web server
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This would return a list of packages where either the name or the description contains the words &amp;ldquo;web&amp;rdquo; and &amp;ldquo;server&amp;rdquo;, allowing you to browse different options like apache2, nginx, or lighttpd.&lt;/p&gt;
&lt;h4 id=&#34;apt-install&#34;&gt;apt install&lt;/h4&gt;
&lt;p&gt;Downloads the &lt;code&gt;.deb&lt;/code&gt; packages to &lt;code&gt;/var/cache/apt/archives/&lt;/code&gt; and then calls &lt;code&gt;dpkg&lt;/code&gt; to extract and install them on the system. It also ensures that the packages are installed in the correct order to satisfy the dependencies.&lt;/p&gt;
&lt;h4 id=&#34;apt-clean&#34;&gt;apt clean&lt;/h4&gt;
&lt;p&gt;Clears out the local cache of downloaded package files from /var/cache/apt/archives/. This is useful for freeing up disk space, especially in storage-constrained environments like Docker containers or after you&amp;rsquo;ve finished a large batch of installations.&lt;/p&gt;
&lt;h4 id=&#34;apt-list&#34;&gt;apt list&lt;/h4&gt;
&lt;p&gt;Lists all packages known to APT from the local cache. You can use flags like &amp;ndash;installed to see only the packages currently on your system or &amp;ndash;upgradable to see what can be upgraded. It&amp;rsquo;s also powerful when combined with tools like grep to find specific packages.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Diving into PHP: the fun of custom extensions</title>
      <link>https://lzag.dev/diving-into-php-the-fun-of-custom-extensions/</link>
      <pubDate>Tue, 24 Jun 2025 00:07:51 +0000</pubDate>
      <guid>https://lzag.dev/diving-into-php-the-fun-of-custom-extensions/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://lzag.dev/wp-content/uploads/2025/06/php_extension-removebg-preview-e1750805935239.png&#34; alt=&#34;&#34;&gt;&lt;br&gt;
Inspired by a chapter on compiling from source in &lt;a href=&#34;https://www.amazon.com/How-Linux-Works-Brian-Ward/dp/1718500408&#34; title=&#34;How Linux Works&#34;&gt;How Linux Works&lt;/a&gt;, I decided to practice a bit by building a PHP extension. Compiling programs from source is a valuable skill, and what better way to practice than by extending everybody&amp;rsquo;s favourite language, PHP? This post explores creating a simple PHP extension to add a function that wraps a string in a decorative border and experiment with creating a new class.&lt;/p&gt;</description>
      <content>&lt;p&gt;&lt;img src=&#34;https://lzag.dev/wp-content/uploads/2025/06/php_extension-removebg-preview-e1750805935239.png&#34; alt=&#34;&#34;&gt;&lt;br&gt;
Inspired by a chapter on compiling from source in &lt;a href=&#34;https://www.amazon.com/How-Linux-Works-Brian-Ward/dp/1718500408&#34; title=&#34;How Linux Works&#34;&gt;How Linux Works&lt;/a&gt;, I decided to practice a bit by building a PHP extension. Compiling programs from source is a valuable skill, and what better way to practice than by extending everybody&amp;rsquo;s favourite language, PHP? This post explores creating a simple PHP extension to add a function that wraps a string in a decorative border and experiment with creating a new class.&lt;/p&gt;
&lt;h4 id=&#34;what-are-php-extensions&#34;&gt;What are PHP extensions?&lt;/h4&gt;
&lt;p&gt;PHP extensions are compiled libraries that enhance PHP&amp;rsquo;s core functionality. Written in C or C++, they integrate with the PHP engine to provide new features, boost performance, or connect to external systems.&lt;/p&gt;
&lt;p&gt;Writing a PHP extension is useful when you need to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add unique functionality not in PHP&amp;rsquo;s standard library.&lt;/li&gt;
&lt;li&gt;Improve performance for compute-intensive tasks.&lt;/li&gt;
&lt;li&gt;Interface with custom or low-level libraries.&lt;/li&gt;
&lt;li&gt;Optimize specific application workflows.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Why not just create a standard PHP library, you ask? Well, creating an extension gives us access to pretty low level PHP plumbing, so when we want to squeeze every little bit of performance from the language that might be the best way. And didactically it&amp;rsquo;s also a great way to learn how PHP works behind the scenes.&lt;/p&gt;
&lt;h4 id=&#34;project-goal&#34;&gt;Project goal&lt;/h4&gt;
&lt;p&gt;The goal is to create a PHP extension with:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A &lt;code&gt;printLuLu(string, length=10)&lt;/code&gt; function that prints a string framed by alternating &lt;code&gt;=&lt;/code&gt; and &lt;code&gt;-&lt;/code&gt; lines.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;PrintLu&lt;/code&gt; class with two methods:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;printWrap(string, length=10)&lt;/code&gt;: Prints a string with the same border.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getLuLen(string)&lt;/code&gt;: Returns the string&amp;rsquo;s length.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;getting-started&#34;&gt;Getting started&lt;/h4&gt;
&lt;p&gt;I followed tutorials from &lt;a href=&#34;https://www.phpinternalsbook.com/&#34;&gt;PHP Internals Book&lt;/a&gt; and &lt;a href=&#34;https://www.zend.com/resources/writing-php-extensions&#34;&gt;Zend&amp;rsquo;s guide&lt;/a&gt;. The former is detailed but slightly outdated, while the latter is more recent but less in-depth.&lt;/p&gt;
&lt;h5 id=&#34;php-vs-zend-extension&#34;&gt;PHP vs. Zend Extension&lt;/h5&gt;
&lt;p&gt;PHP extensions add user-facing functionality, while Zend extensions modify the engine itself. Since I&amp;rsquo;m adding a simple function and class, a PHP extension suffices. Extensions can be compiled as shared libraries or statically linked into the PHP binary. I&amp;rsquo;m going with static linking for this one.&lt;/p&gt;
&lt;h4 id=&#34;setting-up&#34;&gt;Setting up&lt;/h4&gt;
&lt;p&gt;First, I cloned a forked PHP source repository: &lt;a href=&#34;https://github.com/lzag/php-src-fork&#34;&gt;php-src-fork&lt;/a&gt; and build PHP from it:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt install -y pkg-config build-essential autoconf bison re2c libxml2-dev libsqlite3-dev
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;./buildconf
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;./configure --enable-debug
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;make -j4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id=&#34;generating-the-extension-skeleton&#34;&gt;Generating the extension skeleton&lt;/h4&gt;
&lt;p&gt;PHP provides a script to create an extension template that generates boilerplate code and saves time on setup:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;./sapi/cli/php ext/ext_skel.php --ext print_lu
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Generated code can be cound in ext/print_lu.&lt;/p&gt;
&lt;h3 id=&#34;navigating-the-code&#34;&gt;Navigating the code&lt;/h3&gt;
&lt;p&gt;PHP extensions make heavy use of macros that simplify many tasks. However, these macros can make code hard to understand sometimes. To check out what gets replaced I can run the preprocessor that would inline the macros code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;gcc -E -P -Imain -I. -IZend -ITSRM ext/print_lu/print_lu.c &amp;gt; comp.c &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; clang-format -i comp.c
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This creates a formatted &lt;code&gt;comp.c&lt;/code&gt; file with expanded macros.&lt;/p&gt;
&lt;h3 id=&#34;writing-tests&#34;&gt;Writing tests&lt;/h3&gt;
&lt;p&gt;PHP&amp;rsquo;s testing framework is &lt;a href=&#34;https://php.github.io/php-src/miscellaneous/writing-tests.html&#34; title=&#34;well-documented&#34;&gt;well-documented&lt;/a&gt;. The skeleton includes sample tests, so it&amp;rsquo;s easy to figure out the structure. I&amp;rsquo;m writing 4 tests:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Check if the &lt;code&gt;print_lu&lt;/code&gt; extension is loaded.&lt;/li&gt;
&lt;li&gt;Test &lt;code&gt;printLuLu&lt;/code&gt; output.&lt;/li&gt;
&lt;li&gt;Test &lt;code&gt;PrintLu::printWrap&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Test &lt;code&gt;PrintLu::getLuLen&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;TEST&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;Check&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;print_lu&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;loaded&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;EXTENSIONS&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;print_lu&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;FILE&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;?&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;php&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;The extension &amp;#34;print_lu&amp;#34; is available&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;?&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;--EXPECT--
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;The extension &amp;#34;print_lu&amp;#34; is available
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;TEST&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;print_lulu&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Basic&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;EXTENSIONS&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;print_lu&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;FILE&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;?&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;php&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;printLuLu&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello, World!&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;printLuLu&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello, World!&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;?&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;--EXPECT--
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;=-=-=-=-=-
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;Hello, World!
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;=-=-=-=-=-
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;=-=-
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;Hello, World!
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;=-=-
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;TEST&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;printWrap&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Basic&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;EXTENSIONS&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;print_lu&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;FILE&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;?&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;php&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$printLu &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;PrintLu&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$printLu&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;printWrap&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello, World!&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;?&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;--EXPECT--
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;=-=-
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;Hello, World!
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;=-=-
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;TEST&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;getLuLen&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Basic&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;EXTENSIONS&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;print_lu&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;FILE&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;?&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;php&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$printLu &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;PrintLu&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$len &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; $printLu&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;getLuLen&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hello World&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;var_dump&lt;/span&gt;($len);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;?&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;--EXPECT--
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;int(11)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Run tests with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;make test TESTS&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ext/print_lu&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Initially, tests were skipped because the extension wasn&amp;rsquo;t loaded, so to enable it, I rebuilt PHP:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;make clean
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;./buildconf
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;./configure --enable-print_lu --enable-debug
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;make -j4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This made the first test pass.&lt;/p&gt;
&lt;h4 id=&#34;implementing-the-function&#34;&gt;Implementing the function&lt;/h4&gt;
&lt;p&gt;Let&amp;rsquo;s implement the first function. We need to add code to print_lu.c as well print_lu.stub.php, removing the old test functions and adding the new definitions. The convenient thing is that a lot of boilerplate code will be generated for us.&lt;/p&gt;
&lt;p&gt;File print_lu.c defines the inner workings of the function using the PHP_FUNCTION macro as well as param parsing macros:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;PHP_FUNCTION(printLuLu)
{
    zend_long line_len = 10;
    char *var;
    size_t var_len;
    size_t i;

    ZEND_PARSE_PARAMETERS_START(1, 2)
            Z_PARAM_STRING(var, var_len)
            Z_PARAM_OPTIONAL
            Z_PARAM_LONG(line_len)
    ZEND_PARSE_PARAMETERS_END();

    for (i = 0; i &amp;lt; line_len; i++) {
        php_printf(&amp;quot;%c&amp;quot;, i % 2 == 0 ? &amp;amp;#039;=&amp;amp;#039; : &amp;amp;#039;-&amp;amp;#039;);
    }
    php_printf(&amp;quot;\n&amp;quot;);

    php_printf(&amp;quot;%s\n&amp;quot;, var);

    for (i = 0; i &amp;lt; line_len; i++) {
            php_printf(&amp;quot;%c&amp;quot;, i % 2 == 0 ? &amp;amp;#039;=&amp;amp;#039; : &amp;amp;#039;-&amp;amp;#039;);
    }
    php_printf(&amp;quot;\n&amp;quot;);

    RETURN_NULL();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;File print_lu.stub.php defines the function interface:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;printLuLu&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;string&lt;/span&gt; $string, &lt;span style=&#34;color:#a6e22e&#34;&gt;int&lt;/span&gt; $len &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;void&lt;/span&gt; {}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This makes our second test pass&lt;/p&gt;
&lt;h3 id=&#34;implementing-the-class&#34;&gt;Implementing the class&lt;/h3&gt;
&lt;p&gt;I added the &lt;code&gt;PrintLu&lt;/code&gt; class to &lt;code&gt;print_lu.stub.php&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;PrintLu&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;printWrap&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;string&lt;/span&gt; $string, &lt;span style=&#34;color:#a6e22e&#34;&gt;int&lt;/span&gt; $len &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;void&lt;/span&gt; {}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;getLuLen&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;string&lt;/span&gt; $string)&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;int&lt;/span&gt; {}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The class methods use the &lt;code&gt;PHP_METHOD&lt;/code&gt; macro:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;PHP_METHOD&lt;/span&gt;(PrintLu, printWrap)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    zend_long line_len &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;var;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;size_t&lt;/span&gt; var_len, i;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;ZEND_PARSE_PARAMETERS_START&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;Z_PARAM_STRING&lt;/span&gt;(var, var_len)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Z_PARAM_OPTIONAL
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;Z_PARAM_LONG&lt;/span&gt;(line_len)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;ZEND_PARSE_PARAMETERS_END&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; line_len; i&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;php_printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%c&amp;#34;&lt;/span&gt;, i &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;=&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;-&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;php_printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;%s&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;, var);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; line_len; i&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;php_printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%c&amp;#34;&lt;/span&gt;, i &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;=&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;-&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;php_printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;RETURN_NULL&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;PHP_METHOD&lt;/span&gt;(PrintLu, getLuLen)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;var;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;size_t&lt;/span&gt; var_len;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;ZEND_PARSE_PARAMETERS_START&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;Z_PARAM_STRING&lt;/span&gt;(var, var_len)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;ZEND_PARSE_PARAMETERS_END&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;RETURN_LONG&lt;/span&gt;(var_len);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;PHP_MINIT_FUNCTION&lt;/span&gt;(print_lu)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    zend_class_entry &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;print_lu_ce &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;register_class_PrintLu&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; SUCCESS;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And now we have all the tests passing!&lt;/p&gt;
&lt;h4 id=&#34;next-steps&#34;&gt;Next steps&lt;/h4&gt;
&lt;p&gt;This is a fairly simple example, but shows that starting writing a simple PHP extension is not overly complicated. Having some basic knowledge of this process allows us to inspect other extensions and understand their inner workings. There&amp;rsquo;s much more that can be done with these extensions, but that&amp;rsquo;s too much to cover in one post. I recommend reading the linked sources for more info and experimenting.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Debugging connections with strace and packet analysis</title>
      <link>https://lzag.dev/debugging-connections-with-strace-and-packet-analysis/</link>
      <pubDate>Tue, 20 May 2025 22:37:37 +0000</pubDate>
      <guid>https://lzag.dev/debugging-connections-with-strace-and-packet-analysis/</guid>
      <description>&lt;h3 id=&#34;debugging-tcp-connections-tools-and-techniques&#34;&gt;Debugging TCP Connections: Tools and Techniques&lt;/h3&gt;
&lt;p&gt;This post explores tools and techniques for debugging TCP connection management, focusing on &lt;code&gt;strace&lt;/code&gt;, &lt;code&gt;tshark&lt;/code&gt; (Wireshark&amp;rsquo;s CLI counterpart), &lt;code&gt;ss&lt;/code&gt;, and comparisons with alternatives like &lt;code&gt;tcpdump&lt;/code&gt; and &lt;code&gt;netstat&lt;/code&gt;. We&amp;rsquo;ll analyze single-threaded and multi-threaded servers, as well as a Vert.x-based server-sent events (SSE) setup, to understand TCP behavior, system calls, and packet exchanges. The code and configurations are available in the following repositories:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Webserver project: &lt;a href=&#34;https://github.com/lzag/webserver/tree/strace&#34;&gt;github.com/lzag/webserver/tree/strace&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;SSE server and client: &lt;a href=&#34;https://github.com/lzag/sse-tcp-server&#34;&gt;github.com/lzag/sse-tcp-server&lt;/a&gt;, &lt;a href=&#34;https://github.com/lzag/sse-tcp-client&#34;&gt;github.com/lzag/sse-tcp-client&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;tools-overview&#34;&gt;Tools Overview&lt;/h4&gt;
&lt;h5 id=&#34;strace&#34;&gt;strace&lt;/h5&gt;
&lt;p&gt;&lt;code&gt;strace&lt;/code&gt; is a powerful tool for tracing system calls made by a program. It’s invaluable for debugging TCP connections by revealing how a server interacts with the kernel, including socket binding, polling, and data transfer. Its output can be overwhelming without filters, so here are key options for effective use:&lt;/p&gt;</description>
      <content>&lt;h3 id=&#34;debugging-tcp-connections-tools-and-techniques&#34;&gt;Debugging TCP Connections: Tools and Techniques&lt;/h3&gt;
&lt;p&gt;This post explores tools and techniques for debugging TCP connection management, focusing on &lt;code&gt;strace&lt;/code&gt;, &lt;code&gt;tshark&lt;/code&gt; (Wireshark&amp;rsquo;s CLI counterpart), &lt;code&gt;ss&lt;/code&gt;, and comparisons with alternatives like &lt;code&gt;tcpdump&lt;/code&gt; and &lt;code&gt;netstat&lt;/code&gt;. We&amp;rsquo;ll analyze single-threaded and multi-threaded servers, as well as a Vert.x-based server-sent events (SSE) setup, to understand TCP behavior, system calls, and packet exchanges. The code and configurations are available in the following repositories:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Webserver project: &lt;a href=&#34;https://github.com/lzag/webserver/tree/strace&#34;&gt;github.com/lzag/webserver/tree/strace&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;SSE server and client: &lt;a href=&#34;https://github.com/lzag/sse-tcp-server&#34;&gt;github.com/lzag/sse-tcp-server&lt;/a&gt;, &lt;a href=&#34;https://github.com/lzag/sse-tcp-client&#34;&gt;github.com/lzag/sse-tcp-client&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;tools-overview&#34;&gt;Tools Overview&lt;/h4&gt;
&lt;h5 id=&#34;strace&#34;&gt;strace&lt;/h5&gt;
&lt;p&gt;&lt;code&gt;strace&lt;/code&gt; is a powerful tool for tracing system calls made by a program. It’s invaluable for debugging TCP connections by revealing how a server interacts with the kernel, including socket binding, polling, and data transfer. Its output can be overwhelming without filters, so here are key options for effective use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-Y&lt;/code&gt;: Print command names for PIDs.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-y&lt;/code&gt;: Decode file descriptors (FDs).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-T&lt;/code&gt;: Show time spent in each system call.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-tt&lt;/code&gt;: Include timestamps.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-s &amp;lt;size&amp;gt;&lt;/code&gt;: Set maximum string size (default 32).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-a 40&lt;/code&gt;: Align output for readability.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--syscall-limit&lt;/code&gt;: Limit the number of syscalls displayed.&lt;/li&gt;
&lt;li&gt;Filtering: Use &lt;code&gt;%net&lt;/code&gt; for network-related calls and &lt;code&gt;%desc&lt;/code&gt; for file descriptor-related calls to focus on TCP-relevant syscalls. You can also filter by specific syscalls, groups, regex, FD numbers, status (successful/unsuccessful), or paths.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Timing data from &lt;code&gt;-T&lt;/code&gt; is critical for identifying slow syscalls, making &lt;code&gt;strace&lt;/code&gt; ideal for performance optimization.&lt;/p&gt;
&lt;h5 id=&#34;tshark&#34;&gt;tshark&lt;/h5&gt;
&lt;p&gt;&lt;code&gt;tshark&lt;/code&gt; is the command-line interface for Wireshark, offering detailed packet capture and analysis. It’s user-friendly with well-formatted output, making it a preferred alternative to &lt;code&gt;tcpdump&lt;/code&gt;. Key options include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-i &amp;lt;interface&amp;gt;&lt;/code&gt;: Specify the network interface (e.g., &lt;code&gt;lo&lt;/code&gt; for loopback).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-f &amp;quot;&amp;lt;filter&amp;gt;&amp;quot;&lt;/code&gt;: Use Berkeley Packet Filter (BPF) syntax to capture specific packets (e.g., &lt;code&gt;tcp port 9090&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;tshark&lt;/code&gt; reveals packet-level details like connection setup (SYN-ACK), data transfer, and teardown (FIN-ACK), helping diagnose issues like slow responses or connection delays.&lt;/p&gt;
&lt;h5 id=&#34;ss&#34;&gt;ss&lt;/h5&gt;
&lt;p&gt;&lt;code&gt;ss&lt;/code&gt; is a modern replacement for &lt;code&gt;netstat&lt;/code&gt;, offering more options for analyzing socket states. It’s particularly useful for monitoring TCP send and receive queues. A sample command to monitor connections on port 8080, refreshing every second:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;watch -n 1 &#39;ss -tn state all &amp;quot;( sport = :8080 or dport = :8080 )&amp;quot;&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This command filters TCP connections by port and displays queue sizes, helping identify bottlenecks in data flow.&lt;/p&gt;
&lt;h5 id=&#34;alternatives&#34;&gt;Alternatives&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;tcpdump&lt;/strong&gt;: A lightweight packet capture tool, but its output is less formatted than &lt;code&gt;tshark&lt;/code&gt;, making it harder to parse.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;netstat&lt;/strong&gt;: A traditional tool for socket analysis, now largely replaced by &lt;code&gt;ss&lt;/code&gt; due to its limited options and slower performance.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;analyzing-a-threaded-webserver&#34;&gt;Analyzing a Threaded Webserver&lt;/h4&gt;
&lt;p&gt;Using the webserver project (&lt;a href=&#34;https://github.com/lzag/webserver/tree/strace&#34;&gt;GitHub branch: strace&lt;/a&gt;), we’ll compare single-threaded and multi-threaded versions to understand TCP connection handling. The setup uses Docker Compose to run the server and client, with &lt;code&gt;strace&lt;/code&gt; and &lt;code&gt;tshark&lt;/code&gt; executed inside containers.&lt;/p&gt;
&lt;h5 id=&#34;setup-commands&#34;&gt;Setup Commands&lt;/h5&gt;
&lt;p&gt;To initiate the client and capture packets run these command in 3 separate terminals:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;INDEX_FILE=single.php docker compose up or INDEX_FILE=multi.php docker compose up
docker compose exec php-server php client.php
docker compose exec php-server tshark -i lo -f &amp;quot;tcp port 9090&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h5 id=&#34;strace-analysis&#34;&gt;strace Analysis&lt;/h5&gt;
&lt;p&gt;The server is wrapped in &lt;code&gt;strace&lt;/code&gt; to monitor network (&lt;code&gt;%net&lt;/code&gt;) and file descriptor (&lt;code&gt;%desc&lt;/code&gt;) syscalls. The sequence of operations includes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Socket Binding&lt;/strong&gt;: The server binds to a port.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Polling&lt;/strong&gt;: The server polls for incoming connections.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Request Handling&lt;/strong&gt;: Reads client requests and writes responses.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For the single-threaded server, &lt;code&gt;strace&lt;/code&gt; shows &lt;code&gt;accept&lt;/code&gt; calls with non-blocking flags and timings for each syscall. This helps identify slow operations. In the multi-threaded version, PHP uses &lt;code&gt;pselect&lt;/code&gt; for polling, which slightly alters the syscall pattern but doesn’t significantly change the connection flow (open images in new tab to see the details).&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://lzag.dev/wp-content/uploads/2025/05/server-multi-strace1-e1746911233550.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://lzag.dev/wp-content/uploads/2025/05/server-multi-strace2-e1746912638854.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;The timing data (&lt;code&gt;-T&lt;/code&gt;) highlights which syscalls are performance bottlenecks, guiding optimization efforts. For example, a slow &lt;code&gt;read&lt;/code&gt; or &lt;code&gt;write&lt;/code&gt; syscall might indicate buffer issues or network delays.&lt;/p&gt;
&lt;h3 id=&#34;tshark-analysis&#34;&gt;tshark Analysis&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;tshark&lt;/code&gt; captures the packet exchange:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Connection Setup&lt;/strong&gt;: SYN-ACK pattern establishes the TCP connection.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data Transfer&lt;/strong&gt;: HTTP GET request and response.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Teardown&lt;/strong&gt;: FIN-ACK closes the connection.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In our test, the response took 5 seconds in single-threader server and 10 seconds in multi-threaded one, since we extended the sleep for the test. Comparing single- and multi-threaded servers, &lt;code&gt;tshark&lt;/code&gt; shows similar packet patterns.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://lzag.dev/wp-content/uploads/2025/05/tshark-single-e1746911171248.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://lzag.dev/wp-content/uploads/2025/05/server-multi-tshark.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;debugging-sse-with-vertx&#34;&gt;Debugging SSE with Vert.x&lt;/h4&gt;
&lt;p&gt;The SSE scenario (&lt;a href=&#34;https://github.com/lzag/sse-tcp-server&#34;&gt;GitHub: sse-tcp-server&lt;/a&gt;, &lt;a href=&#34;https://github.com/lzag/sse-tcp-client&#34;&gt;sse-tcp-client&lt;/a&gt;) uses a Vert.x server to stream events, allowing us to debug TCP send/receive queues and flow control. We’ll use &lt;code&gt;strace&lt;/code&gt;, &lt;code&gt;tshark&lt;/code&gt;, and &lt;code&gt;ss&lt;/code&gt; to analyze the setup.&lt;/p&gt;
&lt;h5 id=&#34;strace-analysis-1&#34;&gt;strace Analysis&lt;/h5&gt;
&lt;p&gt;&lt;code&gt;strace&lt;/code&gt; reveals that Vert.x uses &lt;code&gt;epoll&lt;/code&gt; for socket polling, which is more efficient than PHP’s &lt;code&gt;select&lt;/code&gt;.&lt;br&gt;
&lt;img src=&#34;https://lzag.dev/wp-content/uploads/2025/05/epoll.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;ss-analysis&#34;&gt;ss Analysis&lt;/h4&gt;
&lt;p&gt;Using the &lt;code&gt;ss&lt;/code&gt; command, we monitor TCP queues:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;watch -n 1 &#39;ss -tn state all &amp;quot;( sport = :8080 or dport = :8080 )&amp;quot;&#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&#34;https://lzag.dev/wp-content/uploads/2025/05/tcp-e1746911278201.png&#34; alt=&#34;&#34;&gt;&lt;br&gt;
As the SSE client connects, the receive queue fills up initially, followed by the send queue as the server streams events.&lt;/p&gt;
&lt;p&gt;When the server’s TCP window fills, &lt;code&gt;tshark&lt;/code&gt; shows zero-window packets, indicating flow control.&lt;/p&gt;
&lt;p&gt;As the window opens, window update packets appear, and streaming resumes. This demonstrates TCP’s built-in flow control in action.&lt;/p&gt;
&lt;h5 id=&#34;tshark-analysis-1&#34;&gt;tshark Analysis&lt;/h5&gt;
&lt;p&gt;&lt;code&gt;tshark&lt;/code&gt; captures the SSE streamand ss monitors the send and receive queues for server and client:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tshark -i lo -f &amp;quot;tcp port 8080&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Initial Events&lt;/strong&gt;: A few events are sent successfully.&lt;br&gt;
&lt;img src=&#34;https://lzag.dev/wp-content/uploads/2025/05/initialstream.png&#34; alt=&#34;&#34;&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Window Full&lt;/strong&gt;: Zero-window packets halt transmission.&lt;br&gt;
&lt;img src=&#34;https://lzag.dev/wp-content/uploads/2025/05/windowfull.png&#34; alt=&#34;&#34;&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Recovery&lt;/strong&gt;: Window updates allow streaming to resume.&lt;br&gt;
&lt;img src=&#34;https://lzag.dev/wp-content/uploads/2025/05/windowupdate.png&#34; alt=&#34;&#34;&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By adjusting server settings like the TCP send buffer size or send queue size, we can observe their impact on queue behavior and streaming performance.&lt;/p&gt;
&lt;h2 id=&#34;key-insights&#34;&gt;Key Insights&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;strace&lt;/strong&gt;: Essential for tracing syscalls and identifying performance bottlenecks. Use filters (&lt;code&gt;%net&lt;/code&gt;, &lt;code&gt;%desc&lt;/code&gt;) to focus on TCP-related calls.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;tshark&lt;/strong&gt;: Provides detailed packet-level insights, ideal for debugging connection setup, data transfer, and teardown.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ss&lt;/strong&gt;: Monitors TCP queues, revealing flow control dynamics and queue bottlenecks.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;sources&#34;&gt;Sources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.amazon.com/Practical-Packet-Analysis-Wireshark-Real-World/dp/1593278020&#34;&gt;Practical Packet Analysis&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=SUO0rQerpMk&#34;&gt;YouTube: TCP Debugging&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    </item>
    
    <item>
      <title>10 Essential Strategies for High-Performance API Design</title>
      <link>https://lzag.dev/10-essential-strategies-for-high-performance-api-design/</link>
      <pubDate>Sat, 17 May 2025 02:59:43 +0000</pubDate>
      <guid>https://lzag.dev/10-essential-strategies-for-high-performance-api-design/</guid>
      <description>&lt;h5 id=&#34;api-performance-optimizations-techniques-and-insights&#34;&gt;API Performance Optimizations: Techniques and Insights&lt;/h5&gt;
&lt;p&gt;Optimizing API performance is critical for delivering fast, scalable, and reliable services. Below I summarize key strategies with practical insights to enhance your API&amp;rsquo;s efficiency.&lt;/p&gt;
&lt;h5 id=&#34;server-side-caching&#34;&gt;Server-Side Caching&lt;/h5&gt;
&lt;p&gt;Caching reduces server load and accelerates responses by storing frequently accessed data. &lt;strong&gt;Cacheability&lt;/strong&gt; determines which responses can be cached based on their idempotency and freshness. GET requests are typically cacheable, while POST or PUT requests often aren’t due to side effects.&lt;/p&gt;</description>
      <content>&lt;h5 id=&#34;api-performance-optimizations-techniques-and-insights&#34;&gt;API Performance Optimizations: Techniques and Insights&lt;/h5&gt;
&lt;p&gt;Optimizing API performance is critical for delivering fast, scalable, and reliable services. Below I summarize key strategies with practical insights to enhance your API&amp;rsquo;s efficiency.&lt;/p&gt;
&lt;h5 id=&#34;server-side-caching&#34;&gt;Server-Side Caching&lt;/h5&gt;
&lt;p&gt;Caching reduces server load and accelerates responses by storing frequently accessed data. &lt;strong&gt;Cacheability&lt;/strong&gt; determines which responses can be cached based on their idempotency and freshness. GET requests are typically cacheable, while POST or PUT requests often aren’t due to side effects.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Whole Response Caching&lt;/strong&gt;: Store entire API responses in an in-memory store like Redis or Memcached. For an e-commerce API, cache a product catalog to avoid repeated database queries, with an expiration time like 1 hour to balance freshness and performance.&lt;br&gt;
&lt;strong&gt;Fragment Caching&lt;/strong&gt;: Cache specific parts of a response, such as a user profile summary or navigation menu, in dynamic applications. This avoids reprocessing unchanged sections.&lt;/p&gt;
&lt;h5 id=&#34;caching-headers&#34;&gt;Caching Headers&lt;/h5&gt;
&lt;p&gt;HTTP caching headers enable clients and intermediaries, like browsers or CDNs, to reuse cached data, reducing server requests.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cache-Control&lt;/strong&gt;: Defines caching rules, like &lt;code&gt;max-age&lt;/code&gt; for freshness. For example, &lt;code&gt;public, max-age=3600&lt;/code&gt; allows caching for 1 hour.&lt;br&gt;
&lt;strong&gt;ETag&lt;/strong&gt;: A unique identifier for a resource’s version. Clients send the ETag; if it matches, the server returns a 304 Not Modified, avoiding redundant data transfer.&lt;br&gt;
&lt;strong&gt;Last-Modified&lt;/strong&gt;: Indicates the resource’s last update time. Clients check if it has changed since the provided timestamp, also avoiding redundant data transfers.&lt;/p&gt;
&lt;h5 id=&#34;database-optimizations&#34;&gt;Database Optimizations&lt;/h5&gt;
&lt;p&gt;Database queries often bottleneck APIs. Optimize them with these techniques:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Eager Loading&lt;/strong&gt;: Fetch related data in one query to avoid the N+1 query problem, where one query for a resource (e.g. users) triggers additional queries for related data (e.g. posts). For example, when retrieving a list of users, include their posts in a single query instead of separate queries per user, reducing query count from N+1 to one.&lt;br&gt;
&lt;strong&gt;Indexing&lt;/strong&gt;: Add indexes to frequently queried columns, like a user ID in a posts table to speed up searches.&lt;br&gt;
&lt;strong&gt;Connection Pooling&lt;/strong&gt;: Use a pool of reusable database connections to reduce overhead during high traffic.&lt;/p&gt;
&lt;h5 id=&#34;response-size-optimizations&#34;&gt;Response Size Optimizations&lt;/h5&gt;
&lt;p&gt;Smaller responses lower latency and bandwidth usage.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Selective Attribute Loading&lt;/strong&gt;: Return only requested fields. In a REST API allow clients to specify fields via query parameters or use GraphQL for precise data selection.&lt;br&gt;
&lt;strong&gt;Compression&lt;/strong&gt;: Enable Gzip or Brotli to compress payloads like JSON, reducing transfer size.&lt;br&gt;
&lt;strong&gt;Pagination&lt;/strong&gt;: Limit results per page, such as 20 items per request, to prevent oversized responses.&lt;/p&gt;
&lt;h5 id=&#34;workload-parallelization&#34;&gt;Workload Parallelization&lt;/h5&gt;
&lt;p&gt;Concurrency boosts throughput by processing tasks simultaneously. For instance, when aggregating data from multiple external APIs, handle requests in parallel using threads or asynchronous tasks to reduce response time.&lt;/p&gt;
&lt;h5 id=&#34;rate-limiting&#34;&gt;Rate Limiting&lt;/h5&gt;
&lt;p&gt;Rate limiting prevents abuse and ensures fair usage. Set a limit, like 100 requests per 15 minutes per client, to protect your API from overload while maintaining availability.&lt;/p&gt;
&lt;h5 id=&#34;asynchronous-processing&#34;&gt;Asynchronous Processing&lt;/h5&gt;
&lt;p&gt;Offload resource-intensive tasks, like file processing or report generation, to background workers. Use a queue system or background job framework to keep API responses fast.&lt;/p&gt;
&lt;h5 id=&#34;content-delivery-networks-cdns&#34;&gt;Content Delivery Networks (CDNs)&lt;/h5&gt;
&lt;p&gt;CDNs cache content closer to users, reducing latency. For APIs with cacheable responses, configure a CDN to store JSON payloads, minimizing origin server load.&lt;/p&gt;
&lt;h5 id=&#34;load-balancing&#34;&gt;Load Balancing&lt;/h5&gt;
&lt;p&gt;Distribute traffic across multiple servers for reliability and scalability. Use a load balancer to route requests based on server health or load, preventing bottlenecks.&lt;/p&gt;
&lt;h5 id=&#34;protocol-optimizations&#34;&gt;Protocol optimizations&lt;/h5&gt;
&lt;p&gt;Modern protocols can significantly boost API performance over traditional HTTP/1.1. Upgrading to HTTP/2 or HTTP/3 enhances speed by allowing multiple data streams in a single connection and compressing headers. HTTP/3 further improves performance on unstable networks, such as mobile connections. Alternatively, gRPC offers a high-performance option outside standard HTTP, using compact binary formats like Protocol Buffers instead of JSON. This reduces data size and speeds up processing, especially for complex requests.&lt;/p&gt;
&lt;p&gt;By applying these optimizations - caching, database tuning, response size reduction and more - you can significantly improve your API’s performance, delivering a seamless user experience and optimizing resource utilization.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>My Coding Philosophy</title>
      <link>https://lzag.dev/my-coding-philosophy/</link>
      <pubDate>Tue, 29 Apr 2025 12:11:31 +0000</pubDate>
      <guid>https://lzag.dev/my-coding-philosophy/</guid>
      <description>&lt;p&gt;Not long ago, someone asked me about my coding philosophy. I mumbled something semi-coherent at that time, but the question stuck with me and got me thinking - what &lt;em&gt;is&lt;/em&gt; my coding philosophy? Having a clear credo to guide me through crunch times would be invaluable.&lt;br&gt;
After some soul-searching, I boiled it down to five principles that keep me grounded and productive.&lt;/p&gt;
&lt;h4 id=&#34;solve-real-problems&#34;&gt;Solve Real Problems&lt;/h4&gt;
&lt;p&gt;I focus on tackling actual issues - bugs, unmaintainable code, or performance bottlenecks, rather than chasing hypothetical problems. Theoretical debates, like Vim vs. Emacs (Vim, obviously), often waste time when multiple valid solutions exist and personal preference is the deciding factor. By addressing concrete challenges as they arise, I keep progress on track and development rooted in reality.&lt;/p&gt;</description>
      <content>&lt;p&gt;Not long ago, someone asked me about my coding philosophy. I mumbled something semi-coherent at that time, but the question stuck with me and got me thinking - what &lt;em&gt;is&lt;/em&gt; my coding philosophy? Having a clear credo to guide me through crunch times would be invaluable.&lt;br&gt;
After some soul-searching, I boiled it down to five principles that keep me grounded and productive.&lt;/p&gt;
&lt;h4 id=&#34;solve-real-problems&#34;&gt;Solve Real Problems&lt;/h4&gt;
&lt;p&gt;I focus on tackling actual issues - bugs, unmaintainable code, or performance bottlenecks, rather than chasing hypothetical problems. Theoretical debates, like Vim vs. Emacs (Vim, obviously), often waste time when multiple valid solutions exist and personal preference is the deciding factor. By addressing concrete challenges as they arise, I keep progress on track and development rooted in reality.&lt;/p&gt;
&lt;h4 id=&#34;master-the-fundamentals&#34;&gt;Master the Fundamentals&lt;/h4&gt;
&lt;p&gt;Great programmers command the basics: data structures, algorithms, and core programming concepts. These foundations enable creative problem-solving and adaptability across tools and languages. For example, studying concurrency in Kotlin deepened my understanding of Swoole coroutines - same principles, different context. Mastery of essentials, like writing clear, reusable functions, takes deliberate practice, not just years of experience. It&amp;rsquo;s about drilling the essentials until they&amp;rsquo;re second nature. Skip this, and you&amp;rsquo;re building castles on sand, no matter how flashy your tech stack. I make sure that my foundation is rock-solid.&lt;/p&gt;
&lt;h4 id=&#34;pursue-excellence&#34;&gt;Pursue excellence&lt;/h4&gt;
&lt;p&gt;Excellence is diving deep into your tools - including reading source code, studying official docs, and knowing their quirks, not hacking together code that &amp;ldquo;just works&amp;rdquo; while your IDE winks at you knowingly. It&amp;rsquo;s easy to hack together spaghetti code and scrape by with surface-level knowledge, especially with AI tools at hand. But I&amp;rsquo;m not content with shallow knowledge and I always seek a profound understanding of whatever I&amp;rsquo;m working with.&lt;/p&gt;
&lt;h4 id=&#34;cultivate-judgement&#34;&gt;Cultivate judgement&lt;/h4&gt;
&lt;p&gt;Judgment grows from experience, like learning to avoid &amp;ldquo;quick fixes&amp;rdquo; that spawn a dozen bugs. It&amp;rsquo;s about choosing the right technology, deciding which code to refactor, and picking tech battles wisely while learning from mistakes. This blend of technical knowledge, practical experience, and honed intuition takes time and practice to develop. I refine my judgment by questioning my assumptions, drawing on expertise and lessons from past errors.&lt;/p&gt;
&lt;h4 id=&#34;focus-on-outcomes&#34;&gt;Focus on Outcomes&lt;/h4&gt;
&lt;p&gt;In the end, it&amp;rsquo;s about shipping it. Success isn&amp;rsquo;t measured by how many design patterns you can name, but about delivering tangible results, whether it&amp;rsquo;s a side project or a work ticket. Intellectual rabbit holes are fun, but they&amp;rsquo;re no match for a working product. A coder who delivers reliably, even if their code isn&amp;rsquo;t poetry, outshines the genius lost in micro-optimizations. I strive to build a reputation for dependability by delivering consistent, impactful results.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Ace Technical Arguments with S.O.F.T.W.A.R.E.</title>
      <link>https://lzag.dev/ace-technical-arguments-with-s-o-f-t-w-a-r-e/</link>
      <pubDate>Fri, 18 Apr 2025 12:42:19 +0000</pubDate>
      <guid>https://lzag.dev/ace-technical-arguments-with-s-o-f-t-w-a-r-e/</guid>
      <description>&lt;p&gt;Technical disagreements are a fundamental part of a programmer&amp;rsquo;s role. Whether debating frameworks, architectures, or the eternal tabs vs. spaces dilemma (spaces, clearly), these discussions can feel like high-stakes battles. However, programming is a collaborative effort - no one owns the codebase unless they&amp;rsquo;re single-handedly building the next unicorn in their garage. For those craving total control, a side project offers the freedom to code like a visionary artist, chasing bold tech dreams (or nightmares). In a team setting, though, it&amp;rsquo;s about sharing the sandbox and finding common ground. To navigate these debates effectively, I&amp;rsquo;ve distilled a practical approach into the &lt;strong&gt;S.O.F.T.W.A.R.E.&lt;/strong&gt; framework, inspired by sales techniques but tailored for technical discussions.&lt;/p&gt;</description>
      <content>&lt;p&gt;Technical disagreements are a fundamental part of a programmer&amp;rsquo;s role. Whether debating frameworks, architectures, or the eternal tabs vs. spaces dilemma (spaces, clearly), these discussions can feel like high-stakes battles. However, programming is a collaborative effort - no one owns the codebase unless they&amp;rsquo;re single-handedly building the next unicorn in their garage. For those craving total control, a side project offers the freedom to code like a visionary artist, chasing bold tech dreams (or nightmares). In a team setting, though, it&amp;rsquo;s about sharing the sandbox and finding common ground. To navigate these debates effectively, I&amp;rsquo;ve distilled a practical approach into the &lt;strong&gt;S.O.F.T.W.A.R.E.&lt;/strong&gt; framework, inspired by sales techniques but tailored for technical discussions.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&#34;study-requirements&#34;&gt;&lt;span style=&#34;font-size: 2em; font-family: &#39;Georgia&#39;&#34;&gt;S&lt;/span&gt;tudy Requirements&lt;/h4&gt;
&lt;p&gt;Before engaging in a debate, uncover the root of the disagreement. Is your colleague fixated on performance, stressed about deadlines, or driven by personal preferences? Ask open-ended questions like, &amp;ldquo;What drives your preference for this approach?&amp;rdquo; or &amp;ldquo;What risks are you aiming to avoid?&amp;rdquo; If they&amp;rsquo;re overlooking critical factors, such as financial implications, gently share the broader context. Align the discussion with company goals - if you&amp;rsquo;re unsure of those, do some reconnaissance. This step is akin to analyzing a codebase before diving in, preventing costly missteps. Sometimes, there&amp;rsquo;s no real disagreement; clarifying requirements may reveal you&amp;rsquo;re already aligned. Always define the problem, outline objectives, and explore all possible options, including those you might not have considered.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&#34;overcome-objections&#34;&gt;&lt;span style=&#34;font-size: 2em; font-family: &#39;Georgia&#39;&#34;&gt;O&lt;/span&gt;vercome Objections&lt;/h4&gt;
&lt;p&gt;With a clear understanding of requirements, identify objections to your proposed solution. Play devil&amp;rsquo;s advocate to stress-test your ideas and anticipate concerns like complexity, cost, or past failures. Validate their worries: &amp;ldquo;I understand your concerns about integration risks.&amp;rdquo; Then, counter with practical solutions. For instance, if a manager fears repeating a failed integration, propose incremental changes to minimize disruption. Break the solution into small, manageable steps and back your case with benchmarks or proof-of-concepts (PoCs). Empirical evidence cuts through subjective debates, much like debugging with hard data instead of guesswork.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&#34;forge-relationships&#34;&gt;&lt;span style=&#34;font-size: 2em; font-family: &#39;Georgia&#39;&#34;&gt;F&lt;/span&gt;orge Relationships&lt;/h4&gt;
&lt;p&gt;Build trust before disagreements escalate. Acknowledge your colleague&amp;rsquo;s expertise: &amp;ldquo;Your optimization last sprint was impressive.&amp;rdquo; Use collaborative language: &amp;ldquo;We both want a scalable system, don&amp;rsquo;t we?&amp;rdquo; A reputation for sound technical decisions earns goodwill, making persuasion easier. Avoid pushing a personal agenda - solutions should prioritize the team&amp;rsquo;s and company&amp;rsquo;s goals, not resume-driven development. Picking battles that benefit the organization carries more weight than championing pet preferences. Strong relationships create a foundation for constructive debates and smoother resolutions.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&#34;tout-benefits&#34;&gt;&lt;span style=&#34;font-size: 2em; font-family: &#39;Georgia&#39;&#34;&gt;T&lt;/span&gt;out Benefits&lt;/h4&gt;
&lt;p&gt;Don&amp;rsquo;t just pitch technical details (e.g., &amp;ldquo;It&amp;rsquo;s microservices!&amp;rdquo;). Focus on outcomes that resonate with your audience: &amp;ldquo;This isolates services, cutting debugging time significantly.&amp;rdquo; Tailor your pitch to their priorities - maintainability for engineers, cost-efficiency for business stakeholders, or faster delivery for project managers. For example, if a manager doubts a new tool, highlight how it reduces development time, backed by data if possible. Numbers speak louder than opinions, so tap into financial metrics or performance stats when relevant. Touting benefits is like showcasing a product&amp;rsquo;s impact, not its code, and requires hitting the right notes for your audience.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&#34;work-out-a-win-win&#34;&gt;&lt;span style=&#34;font-size: 2em; font-family: &#39;Georgia&#39;&#34;&gt;W&lt;/span&gt;ork Out a Win-Win&lt;/h4&gt;
&lt;p&gt;Treat technical disagreements as opportunities for collaboration, not battles. Aim for a hybrid solution: &amp;ldquo;Let&amp;rsquo;s adopt my backend approach but retain your frontend design.&amp;rdquo; If compromise isn&amp;rsquo;t feasible, ensure the other party feels heard: &amp;ldquo;I&amp;rsquo;ll document your concerns for review if issues arise.&amp;rdquo; A win-win preserves team harmony and fosters goodwill. This approach mirrors designing software where everyone benefits from the outcome.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&#34;alleviate-risk&#34;&gt;&lt;span style=&#34;font-size: 2em; font-family: &#39;Georgia&#39;&#34;&gt;A&lt;/span&gt;lleviate Risk&lt;/h4&gt;
&lt;p&gt;Every technical solution carries risks, and depending on your company&amp;rsquo;s risk tolerance, this can be a significant concern. Poor tech choices can be costly or even catastrophic. Mitigate fears by proposing low-risk trials: &amp;ldquo;Let&amp;rsquo;s test this on one module for a sprint.&amp;rdquo; Offer to demo or prototype to address concerns about complexity or learning curves. Like a free trial in sales, a risk-free pilot builds confidence and lets data drive the decision. If your solution succeeds, great; if not, pivot without drama. Alleviating risk is about creating a safe environment to test ideas, much like trialing software before full adoption.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&#34;reference-experts&#34;&gt;&lt;span style=&#34;font-size: 2em; font-family: &#39;Georgia&#39;&#34;&gt;R&lt;/span&gt;eference Experts&lt;/h4&gt;
&lt;p&gt;Strengthen your case with credible sources: &amp;ldquo;This pattern reduced latency by 30% in [project].&amp;rdquo; Cite industry standards, whitepapers, or open-source examples to add weight to your argument. If the debate stalls, consult a neutral third party, like a senior engineer or external expert, for an unbiased perspective. This reduces perceived risk and grounds the discussion in evidence, similar to referencing trusted authorities in software development. Leveraging others&amp;rsquo; experiences saves time on building PoCs and highlights potential outcomes, making your case more compelling.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&#34;evaluate--escalate--evacuate&#34;&gt;&lt;span style=&#34;font-size: 2em; font-family: &#39;Georgia&#39;&#34;&gt;E&lt;/span&gt;valuate | &lt;span style=&#34;font-size: 2em; font-family: &#39;Georgia&#39;&#34;&gt;E&lt;/span&gt;scalate | &lt;span style=&#34;font-size: 2em; font-family: &#39;Georgia&#39;&#34;&gt;E&lt;/span&gt;vacuate&lt;/h4&gt;
&lt;p&gt;After reaching an agreement, follow up: &amp;ldquo;How&amp;rsquo;s the new setup working? Any tweaks needed?&amp;rdquo; If the solution succeeds, share credit: &amp;ldquo;Thanks for supporting this - it&amp;rsquo;s performing great!&amp;rdquo; If it fails, own the mistake to maintain credibility: &amp;ldquo;My bad, let&amp;rsquo;s fix it.&amp;rdquo; This also helps identify if your advice was ignored, providing leverage for future discussions. Evaluating outcomes is like conducting a post-mortem on a software release, ensuring continuous improvement.&lt;/p&gt;
&lt;p&gt;If agreement proves impossible, escalate to a tech lead or engineering manager, presenting all gathered insights clearly and objectively. However, if your input is consistently disregarded and the environment feels misaligned, consider a change. Not every team or company is the right fit - finding a workplace that matches your work style can reduce conflicts and make debates less frequent and more productive.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h4&gt;
&lt;p&gt;Stay calm and don&amp;rsquo;t take technical debates personally. Not every decision is rational; some stem from irrational preferences. Agree on success criteria upfront - performance, cost, or timelines - to minimize opinion-driven conflicts. When clarity is lacking, compromise or escalate, but always keep the team&amp;rsquo;s shared goals in focus. Above all, stay professional, keep coding, and approach debates with a collaborative mindset.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Rate limiter experiments</title>
      <link>https://lzag.dev/rate-limiter-experiments/</link>
      <pubDate>Wed, 26 Mar 2025 12:59:17 +0000</pubDate>
      <guid>https://lzag.dev/rate-limiter-experiments/</guid>
      <description>&lt;h3 id=&#34;what-is-a-rate-limiter&#34;&gt;What is a Rate Limiter?&lt;/h3&gt;
&lt;p&gt;A rate limiter is a mechanism designed to control the frequency of requests or actions in a system. It ensures that a service doesn’t get overwhelmed by too many requests in a short period, protecting it from overuse or abuse. Imagine a bucket that holds a limited number of tokens - each request takes one, and if the bucket runs dry, further requests are delayed or denied until it refills. Rate limiters are widely used for:&lt;/p&gt;</description>
      <content>&lt;h3 id=&#34;what-is-a-rate-limiter&#34;&gt;What is a Rate Limiter?&lt;/h3&gt;
&lt;p&gt;A rate limiter is a mechanism designed to control the frequency of requests or actions in a system. It ensures that a service doesn’t get overwhelmed by too many requests in a short period, protecting it from overuse or abuse. Imagine a bucket that holds a limited number of tokens - each request takes one, and if the bucket runs dry, further requests are delayed or denied until it refills. Rate limiters are widely used for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Preventing Overload&lt;/strong&gt;: Keeping servers stable under high traffic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt;: Mitigating denial-of-service (DoS) attacks by capping request rates.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fair Usage&lt;/strong&gt;: Ensuring equitable resource access among users or applications.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You might use a rate limiter in APIs, web services, or microservices to maintain performance and reliability. For a broad overview, see &lt;a href=&#34;https://en.wikipedia.org/wiki/Rate_limiting&#34;&gt;Rate Limiting on Wikipedia&lt;/a&gt;. I also drew inspiration from practical examples like &lt;a href=&#34;https://stripe.com/blog/rate-limiters&#34;&gt;Rate Limiters at Stripe&lt;/a&gt; and &lt;a href=&#34;https://www.linkedin.com/pulse/how-rate-limiter-took-down-github-arpit-bhayani/&#34;&gt;How a Rate Limiter Took Down GitHub&lt;/a&gt;, which highlight real-world applications and pitfalls.&lt;/p&gt;
&lt;h3 id=&#34;circuit-breaker&#34;&gt;Circuit Breaker&lt;/h3&gt;
&lt;p&gt;While rate limiters control request rates, circuit breakers handle failure resilience. A circuit breaker monitors a service for failures (e.g., timeouts, errors) and “trips” to block requests when a threshold is crossed, preventing cascading failures across a system. Once the service recovers, it &amp;ldquo;closes&amp;rdquo; to resume normal operation. Think of it as a safety switch in your home’s electrical system - cutting power to avoid overload.&lt;/p&gt;
&lt;p&gt;They’re critical in distributed systems where one failing component can drag down others. I explored this concept alongside rate limiting because both contribute to system resilience, a topic well-covered in tools like &lt;a href=&#34;https://resilience4j.readme.io/docs/circuitbreaker&#34;&gt;Resilience4j&lt;/a&gt;, which I investigated during my experiments.&lt;/p&gt;
&lt;h3 id=&#34;concurrency-limiter&#34;&gt;Concurrency Limiter&lt;/h3&gt;
&lt;p&gt;Before diving into my solutions, I also tackled a concurrency limiter. Unlike a rate limiter (which caps requests over time), a concurrency limiter restricts the number of simultaneous active requests. It’s perfect for scenarios where a service can only handle a limited number of concurrent resource-heavy operations. I implemented this alongside my rate limiter because both address resilience: one prevents overload over time, the other in the moment.&lt;/p&gt;
&lt;h3 id=&#34;why-a-hands-on-approach&#34;&gt;Why a Hands-On Approach?&lt;/h3&gt;
&lt;p&gt;I decided to build a rate limiter from scratch because abstract discussions only get you so far. Coding a proof of concept (PoC) forces you to wrestle with real-world details - headers, concurrency, exceptions - that don’t surface in theory. It’s the best way to clarify technical expectations and refine requirements. Plus, I wanted to explore how rate limiting ties into broader resilience strategies, like circuit breaking, in a tangible way. Resources like &lt;a href=&#34;https://www.figma.com/blog/an-alternative-approach-to-rate-limiting/&#34;&gt;An Alternative Approach to Rate Limiting&lt;/a&gt; and &lt;a href=&#34;https://blog.logrocket.com/rate-limiting-node-js/&#34;&gt;Rate Limiting in Node.js&lt;/a&gt; fueled my curiosity to experiment hands-on.&lt;/p&gt;
&lt;h3 id=&#34;implementation-challenges-going-distributed&#34;&gt;Implementation Challenges: Going Distributed&lt;/h3&gt;
&lt;p&gt;A rate limiter in a single app is straightforward - you can track requests in memory with a counter. But in a distributed system, where multiple apps or instances serve requests, that approach falls apart. Each app might have its own count, leading to inconsistent limits. For true rate limiting, you need a centralized system that all instances can query, ensuring a unified view of request rates.&lt;/p&gt;
&lt;p&gt;This is where distributed design becomes critical. I found &lt;a href=&#34;https://youtu.be/FU4WlwfS3G0?feature=shared&#34;&gt;Understanding Rate Limiting (YouTube)&lt;/a&gt; incredibly helpful for grasping these challenges. It emphasizes the need for a shared store - like Redis - to synchronize limits across services, a strategy also detailed in &lt;a href=&#34;https://github.blog/engineering/how-we-scaled-github-api-sharded-replicated-rate-limiter-redis/&#34;&gt;Scaling GitHub’s API with a Sharded, Replicated Rate Limiter in Redis&lt;/a&gt;, which I referenced for inspiration.&lt;/p&gt;
&lt;h3 id=&#34;my-solutions&#34;&gt;My Solutions&lt;/h3&gt;
&lt;p&gt;To tackle these challenges, I leaned heavily on Redis for its speed and atomicity, guided by &lt;a href=&#34;https://redis.io/docs/latest/develop/use/patterns/rate-limiting/&#34;&gt;Redis’s rate limiting patterns&lt;/a&gt;. Here’s how I approached it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Centralized Storage&lt;/strong&gt;: I stored all rate-limiting state in Redis, making it accessible to any app in the system. This ensures consistent limits across distributed instances.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scripts in Redis&lt;/strong&gt;: I embedded the core logic (e.g., token bucket refills, counter updates) in Redis Lua scripts. This minimizes network calls - everything happens in one atomic operation on the server side. For example, my token bucket checks tokens, updates counts, and sets expiration in a single script.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Concurrency Limiter&lt;/strong&gt;: I implemented this with a simple counter (decremented when requests end) and a timestamp set for cleanup, all managed in Redis with termination middleware to avoid leaks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Refill Strategy&lt;/strong&gt;: I opted for an application-scheduled Redis script (&lt;code&gt;schedulePeriodic&lt;/code&gt;) over periodic &lt;code&gt;mset&lt;/code&gt; calls or Redis-side cron jobs. This gave me control over errors, visibility into timing (via &lt;code&gt;getNextTimer&lt;/code&gt;), and atomic execution without excessive network overhead.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All scripts and configs are in my &lt;a href=&#34;https://github.com/lzag/rate-limiter&#34;&gt;rate-limiter repo&lt;/a&gt;. The trade-off? Managing Lua scripts adds complexity, but their simplicity - akin to &lt;a href=&#34;https://gist.github.com/ptarjan/e38f45f2dfe601419ca3af937fff574d&#34;&gt;Simple Rate Limiting Algorithm Gist&lt;/a&gt; - keeps bugs at bay, and the performance gains are worth it.&lt;/p&gt;
&lt;h2 id=&#34;rate-limiter-algorithm-descriptions&#34;&gt;Rate Limiter Algorithm Descriptions&lt;/h2&gt;
&lt;h5 id=&#34;token-bucket&#34;&gt;Token Bucket&lt;/h5&gt;
&lt;p&gt;This algorithm maintains a fixed number of tokens (maxRequests) in a bucket that refills at a constant rate. Each request consumes one token, and if tokens remain (remaining &amp;gt;= 0), the request is allowed. The bucket is refilled by a Lua script executed periodically by the app. Learn more about its mechanics at &lt;a href=&#34;https://en.wikipedia.org/wiki/Token_bucket#Algorithm&#34;&gt;Token Bucket Algorithm&lt;/a&gt;.&lt;br&gt;
&lt;img src=&#34;https://lzag.dev/wp-content/uploads/2025/03/token_bucket.png&#34; alt=&#34;token bucket results&#34;&gt;&lt;/p&gt;
&lt;h5 id=&#34;timestamp-token-bucket&#34;&gt;Timestamp Token Bucket&lt;/h5&gt;
&lt;p&gt;An enhanced token bucket that uses timestamps to calculate token replenishment dynamically. It tracks the last request time and adds tokens based on elapsed time relative to the window size, capping at maxRequests. Requests are allowed if tokens remain after consumption. In the current implementation, the user starts accruing tokens from the first request.&lt;/p&gt;
&lt;h5 id=&#34;fractional-token-bucket&#34;&gt;Fractional Token Bucket&lt;/h5&gt;
&lt;p&gt;A variation of the token bucket that divides the maxRequests into smaller fractional tokens (e.g., 1/60th). Tokens replenish incrementally over time based on elapsed time, allowing finer granularity. Requests are permitted if fractional tokens are available.&lt;/p&gt;
&lt;h5 id=&#34;leaky-bucket&#34;&gt;Leaky Bucket&lt;/h5&gt;
&lt;p&gt;This algorithm treats requests as water flowing into a bucket that leaks at a constant rate (maxRequests/windowSize). It tracks the number of requests (count) and removes them over time. If the bucket isn’t full (count &amp;lt;= maxRequests), the request is allowed.&lt;/p&gt;
&lt;h5 id=&#34;fixed-window-counter&#34;&gt;Fixed Window Counter&lt;/h5&gt;
&lt;p&gt;This divides time into fixed windows (e.g., seconds) and tracks requests per window using a concatenated key. Each request decrements the counter, and the window resets after expiration. Requests are allowed if the counter hasn’t hit zero.&lt;/p&gt;
&lt;h5 id=&#34;sliding-window-log&#34;&gt;Sliding Window Log&lt;/h5&gt;
&lt;p&gt;This logs each request’s timestamp in a Redis sorted set and removes entries outside the sliding window. It counts requests within the window, allowing new ones if the total is below maxRequests. The log expires after the window duration.&lt;br&gt;
&lt;img src=&#34;https://lzag.dev/wp-content/uploads/2025/03/sliding_window_log.png&#34; alt=&#34;sliding window log results&#34;&gt;&lt;/p&gt;
&lt;h5 id=&#34;sliding-window-counter&#34;&gt;Sliding Window Counter&lt;/h5&gt;
&lt;p&gt;This approximates a sliding window by dividing it into sub-windows (e.g., 1/60th of windowSize) and tracking request counts per sub-window. Old sub-windows are pruned, and requests are allowed if the total count across the window is below maxRequests.&lt;img src=&#34;https://lzag.dev/wp-content/uploads/2025/03/sliding_window_counter-1.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h5 id=&#34;comparison-table&#34;&gt;Comparison table&lt;/h5&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Algorithm&lt;/th&gt;
          &lt;th&gt;Granularity&lt;/th&gt;
          &lt;th&gt;Storage Method&lt;/th&gt;
          &lt;th&gt;Time Handling&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;Token Bucket&lt;/td&gt;
          &lt;td&gt;Coarse&lt;/td&gt;
          &lt;td&gt;Single key (SETEX)&lt;/td&gt;
          &lt;td&gt;Fixed expiration&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Timestamp Token Bucket&lt;/td&gt;
          &lt;td&gt;Medium&lt;/td&gt;
          &lt;td&gt;Hash (HSET)&lt;/td&gt;
          &lt;td&gt;Timestamp-based&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Fractional Token Bucket&lt;/td&gt;
          &lt;td&gt;Fine&lt;/td&gt;
          &lt;td&gt;Hash (HSET)&lt;/td&gt;
          &lt;td&gt;Timestamp-based&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Leaky Bucket&lt;/td&gt;
          &lt;td&gt;Medium&lt;/td&gt;
          &lt;td&gt;Hash (HSET)&lt;/td&gt;
          &lt;td&gt;Timestamp-based&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Fixed Window Counter&lt;/td&gt;
          &lt;td&gt;Coarse&lt;/td&gt;
          &lt;td&gt;Concatenated key (SETEX)&lt;/td&gt;
          &lt;td&gt;Window index&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Sliding Window Log&lt;/td&gt;
          &lt;td&gt;Fine&lt;/td&gt;
          &lt;td&gt;Sorted set (ZADD)&lt;/td&gt;
          &lt;td&gt;Timestamp-based&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Sliding Window Counter&lt;/td&gt;
          &lt;td&gt;Medium&lt;/td&gt;
          &lt;td&gt;Hash (HSET)&lt;/td&gt;
          &lt;td&gt;Sub-window index&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;wrapping-up-its-all-about-resilience&#34;&gt;Wrapping Up: It’s All About Resilience&lt;/h3&gt;
&lt;p&gt;This project was about more than just rate limiting - it’s about building resilient systems. Rate limiters prevent overload, concurrency limiters manage real-time capacity, and circuit breakers handle failures. By implementing these hands-on, I learned how they interplay and how distributed systems demand careful design. Check out the code at &lt;a href=&#34;https://github.com/lzag/rate-limiter&#34;&gt;my rate-limiter repo&lt;/a&gt; and try it yourself.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>The importance of testing in software development</title>
      <link>https://lzag.dev/the-importance-of-testing-in-software-development/</link>
      <pubDate>Fri, 21 Mar 2025 10:52:58 +0000</pubDate>
      <guid>https://lzag.dev/the-importance-of-testing-in-software-development/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://lzag.dev/wp-content/uploads/2025/03/hamlet-1.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;to-test-or-not-to-test&#34;&gt;To test or not to test&lt;/h3&gt;
&lt;p&gt;Testing is the bedrock of solid software development. It’s not just about squashing bugs, it’s about crafting a foundation that keeps your code dependable, maintainable, and a breeze to work with. From my own experience, I’ve noticed not every developer buys into testing. Some rock a YOLO mindset, thriving on the thrill of tossing bugs into the wild and patching them later, rather than building a sturdy base of tested code. Skip the tests, and you’re flirting with a hulking monolith — devs start piling on new code instead of tweaking what’s there, terrified of shattering some vital piece. Left unchecked, this spirals into an untamable dumpster fire. To me, writing tests feels like a no-brainer, yet I keep running into devs who resist the idea. Here’s a rundown of the most common objections I’ve heard:&lt;/p&gt;</description>
      <content>&lt;p&gt;&lt;img src=&#34;https://lzag.dev/wp-content/uploads/2025/03/hamlet-1.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;to-test-or-not-to-test&#34;&gt;To test or not to test&lt;/h3&gt;
&lt;p&gt;Testing is the bedrock of solid software development. It’s not just about squashing bugs, it’s about crafting a foundation that keeps your code dependable, maintainable, and a breeze to work with. From my own experience, I’ve noticed not every developer buys into testing. Some rock a YOLO mindset, thriving on the thrill of tossing bugs into the wild and patching them later, rather than building a sturdy base of tested code. Skip the tests, and you’re flirting with a hulking monolith — devs start piling on new code instead of tweaking what’s there, terrified of shattering some vital piece. Left unchecked, this spirals into an untamable dumpster fire. To me, writing tests feels like a no-brainer, yet I keep running into devs who resist the idea. Here’s a rundown of the most common objections I’ve heard:&lt;/p&gt;
&lt;h4 id=&#34;common-objections-to-testing-and-counter-arguments&#34;&gt;Common Objections to Testing (and Counter-Arguments)&lt;/h4&gt;
&lt;h5 id=&#34;requirements-flip-and-tests-explode&#34;&gt;&amp;ldquo;REQUIREMENTS FLIP AND TESTS EXPLODE&amp;rdquo;&lt;/h5&gt;
&lt;p&gt;Tests &lt;em&gt;should&lt;/em&gt; break when requirements evolve — they’re there to flag when logic no longer aligns with expectations. If the business rules stay put, but you’re still rewriting tests for every new feature, that’s a sign your tests suck, likely because they&amp;rsquo;re overly focused on implementation details instead of the big-picture behavior. The problem isn’t testing itself — it’s how you’re doing it.&lt;/p&gt;
&lt;h5 id=&#34;writing-tests-takes-more-time-than-downloading-windows-updates&#34;&gt;&amp;ldquo;WRITING TESTS TAKES MORE TIME THAN DOWNLOADING WINDOWS UPDATES&amp;rdquo;&lt;/h5&gt;
&lt;p&gt;Track your bugs religiously. We often lowball how long bug fixes take, and honest tracking reveals the real cost in time. If your bug-fix rate’s at zero, I’ll eat my words, but I’ve never seen a reliable codebase without solid test coverage. Plus, manual testing during dev? Total time vampire. Automating it speeds things up and makes code friendlier for the next person — no more secret voodoo in one dev’s head. As a manager be careful of rewarding the “bug hero” who swoops in for quick fixes, ignoring the devs churning out stable stuff.&lt;/p&gt;
&lt;h5 id=&#34;tests-on-bugs-still-laughing&#34;&gt;&amp;ldquo;TESTS ON, BUGS STILL LAUGHING&amp;rdquo;&lt;/h5&gt;
&lt;p&gt;Testing’s an art you refine over time. You learng what&amp;rsquo;s absolutely crucial to test and what you can comfortably skip. I’ve seen tests so over-mocked they’re just flexing on a lone &lt;code&gt;if&lt;/code&gt; statement in a sea of code. It takes practice - don’t expect gold right out of the gate.&lt;/p&gt;
&lt;h5 id=&#34;testings-not-my-gig&#34;&gt;&amp;ldquo;TESTING&amp;rsquo;S NOT MY GIG&amp;rdquo;&lt;/h5&gt;
&lt;p&gt;Some developers view testing as a QA task, but this separation increases effort . You’re already running the code to check it why not write a test to do that instead of poking it manually? Also, track the times when a ticket comes back with “QA failed,” and you have to restart development and test everything again, manually.&lt;/p&gt;
&lt;h5 id=&#34;no-tests-no-tools-no-clue-no-hope&#34;&gt;&amp;ldquo;NO TESTS, NO TOOLS, NO CLUE, NO HOPE&amp;rdquo;&lt;/h5&gt;
&lt;p&gt;In startups, speed often trumps quality. Fair enough to get to market, but as a company grows, bugs get pricier. Early on, it’s all about shipping; later, customers demand polish. Without tests, the code balloons, and there’s never a perfect moment to bring in tools or training. Managers, track defects and delivery hiccups. Carve out time for key tests and build team skills gradually - it’s a future-proof investment. Reward stable, tested code, too.&lt;/p&gt;
&lt;h5 id=&#34;management-couldnt-care-less&#34;&gt;&amp;ldquo;MANAGEMENT COULDN’T CARE LESS&amp;rdquo;&lt;/h5&gt;
&lt;p&gt;They often don’t - until disaster strikes. Devs need to step forward, putting risks in language they get: measure time spent on bugs, note late deliveries, and tie it to financial impact. Management may not comprehend the code’s state, imagining tests cover it or underestimating fixable flaws. If after all they’re unbothered by chaos and you’re driven to fix it, you’re at a crossroads - either suck it up or jump ship. Code quality is a strategic decision - not all prioritize excellence, weighing it as a cost versus a long-term gain.&lt;/p&gt;
&lt;h5 id=&#34;tests-are-for-wimps-yolo&#34;&gt;&amp;ldquo;TESTS ARE FOR WIMPS, YOLO&amp;rdquo;&lt;/h5&gt;
&lt;p&gt;YOLO’s cool if your shop’s a madhouse. Otherwise, I bet you’re just posturing, stacking code like a Jenga tower, chanting “don’t touch it” while it wobbles. Refactoring? What&amp;rsquo;s that?&lt;/p&gt;
&lt;h5 id=&#34;my-codes-a-god---tests-cant-touch-it&#34;&gt;&amp;ldquo;MY CODE’S A GOD - TESTS CAN’T TOUCH IT&amp;rdquo;&lt;/h5&gt;
&lt;p&gt;Tests? Pfft - those are for peasants, not your celestial genius, right? If you’re really the Michelangelo of coding, maybe I’m wrong, but really? (Track those bugs and prove it.)&lt;/p&gt;
&lt;h5 id=&#34;tests-summon-the-code-kraken&#34;&gt;&amp;ldquo;TESTS SUMMON THE CODE KRAKEN&amp;rdquo;&lt;/h5&gt;
&lt;p&gt;That&amp;rsquo;s a valid one! Still, I say it’s a risk worth taking. Pro tip: skip testing on a full moon.&lt;/p&gt;
&lt;h4 id=&#34;how-to-prioritize-testing&#34;&gt;How to Prioritize Testing&lt;/h4&gt;
&lt;p&gt;If you&amp;rsquo;re convinced by my counter-arguments, you might want to dive head-first into blanket-covering your codebase with tests, but hold on. Often, managers push full-on TDD with 100% coverage. That usually doesn’t lead to better-quality code - but neither does 0%. Figuring out this number varies for each codebase and takes &lt;a href=&#34;https://lzag.dev/judgement-in-software-development/&#34; title=&#34;judgment&#34;&gt;judgment&lt;/a&gt;, especially if we’re starting from a low point. So, how do we decide what to test and what not?&lt;/p&gt;
&lt;h5 id=&#34;focus-on-critical-paths&#34;&gt;Focus on Critical Paths&lt;/h5&gt;
&lt;p&gt;What’s the core functionality? Identify the features or flows users rely on most (e.g. login, checkout). If these break, your app’s dead in the water. Also, follow the money: if your code ties directly to revenue (e.g. payment processing, order fulfillment), test that first. A bug there hurts more than a glitch in the settings page.&lt;/p&gt;
&lt;h5 id=&#34;target-high-risk-zones&#34;&gt;Target High-Risk Zones&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Bug-prone code:&lt;/strong&gt; Look at git history or bug trackers — where do fixes cluster? Test the stuff that’s broken before.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Complex logic:&lt;/strong&gt; Prioritize functions with lots of conditionals, loops, or edge cases. Complexity breeds bugs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Refactoring candidates:&lt;/strong&gt; If you plan to rework a module soon, tests give you confidence to make bold changes without breaking things.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&#34;develop-awareness-during-coding&#34;&gt;Develop Awareness During Coding&lt;/h5&gt;
&lt;p&gt;With time, you can build a mental map of the codebase, so whenever the opportunity arises you can spot the problematic areas, quick wins and the best targets.&lt;/p&gt;
&lt;h4 id=&#34;why-testing-is-a-must-have&#34;&gt;Why Testing is a Must-Have&lt;/h4&gt;
&lt;h5 id=&#34;documenting-behavior&#34;&gt;Documenting Behavior&lt;/h5&gt;
&lt;p&gt;Tests act like living documentation. They show exactly how your code is supposed to work, making it clear for anyone (including future you) who needs to understand it. No more guessing what a function does - just run the tests.&lt;/p&gt;
&lt;h5 id=&#34;confidence-in-refactoring&#34;&gt;Confidence in Refactoring&lt;/h5&gt;
&lt;p&gt;Want to clean up that messy codebase? With a good test suite, you can refactor fearlessly. Tests confirm your changes didn’t break anything, so you’re not holding your breath every time you deploy.&lt;/p&gt;
&lt;h5 id=&#34;detect-regressions-and-cut-debugging-time&#34;&gt;Detect Regressions and Cut Debugging Time&lt;/h5&gt;
&lt;p&gt;A test suite catches regressions fast - those sneaky bugs that can creep in even when you &amp;rsquo;re doing only a tiny tweak&amp;rsquo;.” Instead of spending hours (or days) debugging, you pinpoint the issue in minutes.&lt;/p&gt;
&lt;h4 id=&#34;writing-testable-code-the-how-to&#34;&gt;Writing Testable Code: The How-To&lt;/h4&gt;
&lt;p&gt;Testing’s more than tossing assertions around — it’s about crafting code that’s &lt;em&gt;made&lt;/em&gt; to be tested. Here’s how to make it happen:&lt;/p&gt;
&lt;h5 id=&#34;make-testing-a-habit&#34;&gt;Make Testing a Habit&lt;/h5&gt;
&lt;p&gt;Start writing tests from day one. It’s a grind that pays off, forcing you to nail design and edge cases early.&lt;/p&gt;
&lt;h5 id=&#34;cut-dependencies&#34;&gt;Cut Dependencies&lt;/h5&gt;
&lt;p&gt;Fewer dependencies mean fewer headaches. Reduce reliance on third-party libraries or pesky side effects like logging.&lt;/p&gt;
&lt;h5 id=&#34;keep-it-simple&#34;&gt;Keep It Simple&lt;/h5&gt;
&lt;p&gt;Write small, focused functions and use straightforward constructors. Complexity is the enemy of testability — simple code is easier to verify.&lt;/p&gt;
&lt;h5 id=&#34;stick-to-the-law-of-demeter&#34;&gt;Stick to the Law of Demeter&lt;/h5&gt;
&lt;p&gt;Don’t dig too deep into object structures (e.g. &lt;code&gt;obj.getA().getB().doSomething()&lt;/code&gt;). Stick to “tell, don’t ask” to keep your code loosely coupled and testable.&lt;/p&gt;
&lt;h5 id=&#34;avoid-hidden-dependencies-and-global-state&#34;&gt;Avoid Hidden Dependencies and Global State&lt;/h5&gt;
&lt;p&gt;Global variables or sneaky side effects are testing kryptonite. Make dependencies explicit (e.g., pass them in) so your tests can control the environment.&lt;/p&gt;
&lt;h5 id=&#34;favour-generic-methods&#34;&gt;Favour Generic Methods&lt;/h5&gt;
&lt;p&gt;Write reusable, flexible methods over hyper-specific ones. Generics are easier to test because they’re not tied to one-off scenarios.&lt;/p&gt;
&lt;h5 id=&#34;favour-polymorphism-over-conditionals&#34;&gt;Favour Polymorphism Over Conditionals&lt;/h5&gt;
&lt;p&gt;Swap &lt;code&gt;if/else&lt;/code&gt; chains for polymorphic behavior (e.g. interfaces or subclasses). It’s cleaner, more extensible, and lets tests target specific implementations.&lt;/p&gt;
&lt;h5 id=&#34;choose-composition-over-inheritance&#34;&gt;Choose Composition Over Inheritance&lt;/h5&gt;
&lt;p&gt;Inheritance can lock you into rigid hierarchies. Composition gives you flexibility, making it simpler to mock or swap out parts during testing.&lt;/p&gt;
&lt;h4 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h4&gt;
&lt;p&gt;Testable code isn’t just about passing tests — it pushes you to write &lt;em&gt;better&lt;/em&gt; code. You get modular, manageable systems that make sense. Sure, it takes discipline, but the time you save debugging and the confidence you gain refactoring make it worth every second.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Automating Redis cluster setup</title>
      <link>https://lzag.dev/automating-redis-cluster-setup/</link>
      <pubDate>Tue, 18 Mar 2025 00:52:39 +0000</pubDate>
      <guid>https://lzag.dev/automating-redis-cluster-setup/</guid>
      <description>&lt;h3 id=&#34;introduction&#34;&gt;Introduction&lt;/h3&gt;
&lt;p&gt;This post serves as a companion to my &lt;a href=&#34;https://github.com/lzag/redis-cluster&#34; title=&#34;repository&#34;&gt;repository&lt;/a&gt;, which provides an automated setup script for deploying a Redis cluster. Designed to streamline the process of configuring a clustered environment, the script enables users to quickly establish a functional Redis cluster with minimal manual intervention. In this guide, I’ll explain the purpose behind this project, detail the testing process for various configurations, and highlight key Redis capabilities uncovered during development. Additionally, I’ll clarify the differences between standalone and clustered Redis to provide context for when clustering is advantageous.&lt;/p&gt;</description>
      <content>&lt;h3 id=&#34;introduction&#34;&gt;Introduction&lt;/h3&gt;
&lt;p&gt;This post serves as a companion to my &lt;a href=&#34;https://github.com/lzag/redis-cluster&#34; title=&#34;repository&#34;&gt;repository&lt;/a&gt;, which provides an automated setup script for deploying a Redis cluster. Designed to streamline the process of configuring a clustered environment, the script enables users to quickly establish a functional Redis cluster with minimal manual intervention. In this guide, I’ll explain the purpose behind this project, detail the testing process for various configurations, and highlight key Redis capabilities uncovered during development. Additionally, I’ll clarify the differences between standalone and clustered Redis to provide context for when clustering is advantageous.&lt;/p&gt;
&lt;h3 id=&#34;purpose-of-this-project&#34;&gt;Purpose of This Project&lt;/h3&gt;
&lt;p&gt;The motivation for this work stems from my interest in distributed systems and resilient architectures. Redis is renowned for its performance as an in-memory data store, but its clustering feature offers a pathway to scalability and fault tolerance that standalone instances cannot achieve. This project is an experiment in harnessing Redis clustering to distribute data across nodes, manage failures, and simplify deployment through automation. By creating this setup, I aimed to explore Redis’ advanced features—such as sharding, replication, and dynamic scaling—while ensuring the process remains accessible and repeatable.&lt;/p&gt;
&lt;h3 id=&#34;standalone-vs-clustered-redis-key-differences&#34;&gt;Standalone vs. Clustered Redis: Key Differences&lt;/h3&gt;
&lt;p&gt;Redis can operate in two primary modes—standalone and clustered—each suited to different use cases:&lt;/p&gt;
&lt;h4 id=&#34;standalone-redis&#34;&gt;Standalone Redis&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; A single instance hosting the entire dataset in memory.&lt;br&gt;
&lt;strong&gt;Advantages:&lt;/strong&gt; Simple to configure (start with redis-server), offers peak performance for moderate workloads, and avoids network latency.&lt;br&gt;
&lt;strong&gt;Limitations:&lt;/strong&gt; Constrained by the memory and processing capacity of a single machine. Without replication or persistence, a failure results in data loss or downtime.&lt;br&gt;
&lt;strong&gt;Use Case:&lt;/strong&gt; Ideal for small applications or caching where simplicity and low latency are priorities.&lt;/p&gt;
&lt;h4 id=&#34;clustered-redis&#34;&gt;Clustered Redis&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Multiple nodes sharing the dataset, with data sharded across 16,384 slots and optional replication for redundancy.&lt;br&gt;
&lt;strong&gt;Advantages:&lt;/strong&gt; Scales horizontally by adding nodes and handles larger datasets and traffic.&lt;br&gt;
&lt;strong&gt;Limitations:&lt;/strong&gt; Introduces complexity in setup and management, along with potential network overhead between nodes.&lt;br&gt;
&lt;strong&gt;Use Case:&lt;/strong&gt; Suited for large-scale applications requiring fault tolerance, distributed data, and sustained performance under heavy load.&lt;/p&gt;
&lt;h3 id=&#34;testing-configurations-and-exploring-redis-capabilities&#34;&gt;Testing Configurations and Exploring Redis Capabilities&lt;/h3&gt;
&lt;p&gt;To assess the automated setup script and probe Redis’ clustering features, I conducted targeted tests comparing throughput and resilience across different configurations. The experiments utilized containerized Redis instances. Below are the setups, methodologies, and findings:&lt;/p&gt;
&lt;h4 id=&#34;standalone-vs-clustered-throughput&#34;&gt;Standalone vs. Clustered Throughput&lt;/h4&gt;
&lt;p&gt;Configuration: A standalone Redis instance (single container) versus a 6-node clustered setup (three master containers, 1 replica each).&lt;br&gt;
Test: Executed 1,000,000 write operations (SET keyN valueN) on each setup using a benchmarking script, measuring completion time and throughput (operations per second).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Vertx&lt;/strong&gt;:&lt;br&gt;
Standalone mode: 7.22 seconds with 138k/sec throughput&lt;br&gt;
Clustered mode: 13.33 seconds and 75k/sec throughput&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PHP&lt;/strong&gt;:&lt;br&gt;
Standalone: 6.7 seconds and 150k/sec throughput&lt;br&gt;
Clustered mode: 13.72 seconds and 73k/sec throughput&lt;/p&gt;
&lt;h4 id=&#34;outcome&#34;&gt;Outcome&lt;/h4&gt;
&lt;p&gt;The standalone instance achieved higher throughput for small datasets due to zero network overhead, completing the writes faster. The clustered setup, while slower per operation due to slot hashing and inter-node communication, demonstrated inferior performance for 1M operations.&lt;br&gt;
Having investigated a bit more on the topic it makes sense to set up clustered Redis if the volume of operations reaches around 100k or the data doesn&amp;rsquo;t fit into memory of a single instance. Otherwise standalone Redis provides better performance. That calculations could also be influenced by other factors like network latency where cluster can parallelize operations. However load for reads can also be distributed with replication which is possible in both standalone and cluster setups.&lt;br&gt;
Insight: Standalone Redis excels for low-latency, single-node workloads, whereas clustering provides scalability for larger datasets and higher concurrency.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Judgement in software development</title>
      <link>https://lzag.dev/judgement-in-software-development/</link>
      <pubDate>Wed, 12 Mar 2025 12:39:16 +0000</pubDate>
      <guid>https://lzag.dev/judgement-in-software-development/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Judgment in software development is clutch—it’s what separates the code monkeys from the architects. (Grok)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;role-of-judgement-in-software-development&#34;&gt;Role of judgement in software development&lt;/h3&gt;
&lt;p&gt;In software development, judgment is about anticipating more than just today’s requirements—it’s about preparing for tomorrow’s unpredictability. You’re coding not only for current specifications but also for future challenges: will the system scale under load, will it fail unexpectedly, or will it create problems later? This foresight requires balancing intuition with analysis, much like strategizing in a game where the next move isn’t fully visible. Development involves a constant tension between order—such as clean code and design patterns—and chaos, driven by deadlines, bugs, and changing requirements. Judgment provides the balance, guiding you to enforce structure when it’s needed and to tolerate imperfection when time or resources demand it, adapting to uncertainty rather than resisting it.&lt;/p&gt;</description>
      <content>&lt;blockquote&gt;
&lt;p&gt;Judgment in software development is clutch—it’s what separates the code monkeys from the architects. (Grok)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;role-of-judgement-in-software-development&#34;&gt;Role of judgement in software development&lt;/h3&gt;
&lt;p&gt;In software development, judgment is about anticipating more than just today’s requirements—it’s about preparing for tomorrow’s unpredictability. You’re coding not only for current specifications but also for future challenges: will the system scale under load, will it fail unexpectedly, or will it create problems later? This foresight requires balancing intuition with analysis, much like strategizing in a game where the next move isn’t fully visible. Development involves a constant tension between order—such as clean code and design patterns—and chaos, driven by deadlines, bugs, and changing requirements. Judgment provides the balance, guiding you to enforce structure when it’s needed and to tolerate imperfection when time or resources demand it, adapting to uncertainty rather than resisting it.&lt;/p&gt;
&lt;p&gt;Judgment goes beyond technical decisions; it reflects the developer’s character. It’s not just about expertise but also about personal qualities—patience to refactor a messy module, humility to recognize a flawed approach, and courage to challenge impractical demands. The code you write mirrors these traits, revealing both your strengths and limitations through the choices you make. Whether it’s opting for a quick fix or investing in a robust solution, these decisions shape not only the software but also how you grow as a professional, tying technical skill to individual integrity.&lt;/p&gt;
&lt;h3 id=&#34;beyond-numbers&#34;&gt;Beyond numbers&lt;/h3&gt;
&lt;p&gt;In software development, relying solely on metrics can mislead, but avoiding them entirely risks paralysis. For instance, skipping numbers on testing coverage might leave a codebase with no clear standards, while obsessing over a metric like cyclomatic complexity can waste time on stable, unchanged classes that don’t need refactoring. It’s better to have an imperfect measure, like tracking bug frequency over weeks, than no measure at all, as this reveals whether a system truly “works” or just limps along with constant fixes. Numbers provide a starting point, but without judgment to interpret them and adjust for deteriorating systems or shifting priorities, they lose their value, leaving decisions to navigate the gray area of “it depends”.&lt;/p&gt;
&lt;p&gt;Judgment also helps developers separate a task’s inherent business complexity from accidental complexity introduced by technical choices. For example, a payment processing feature might be complex due to regulatory requirements—unavoidable business rules—but poor judgment, like overengineering with an unneeded microservices split, can pile on accidental complexity, such as latency from extra network calls or debugging across services. Metrics like deployment time or error rates can hint at this, but only judgment discerns whether the numbers reflect the task’s nature or self-inflicted issues. By logging decisions—like choosing a simpler stack—and reviewing outcomes, developers can refine their ability to minimize unnecessary complexity, focusing effort where the business demands it most.&lt;/p&gt;
&lt;h3 id=&#34;judgement-calls&#34;&gt;Judgement calls&lt;/h3&gt;
&lt;p&gt;Every day, developers face judgment calls that weave together trade-offs and team dynamics. Prioritizing speed, quality, and cost means knowing when to release a functional feature rather than perfecting it endlessly—for instance, deciding whether to optimize an algorithm now or wait until it slows the system. It’s also about understanding the team, choosing tools like a framework based on their skills and the company’s existing investments, rather than chasing the latest trend. Debugging often relies on instinct—like suspecting a race condition—when logs alone aren’t enough, while scope creep demands the discipline to say “no” or “later” to keep the project on track. Tech debt, meanwhile, is a calculated risk: judgment determines when a shortcut makes sense for a deadline and when it’s wiser to address it early, preventing a cascade of issues later. Below examples highlight the daily decisions developers navigate worldwide.&lt;/p&gt;
&lt;h4 id=&#34;framework-adoption&#34;&gt;Framework Adoption&lt;/h4&gt;
&lt;p&gt;When building a web app, Next.js offers efficient React rendering and long-term scalability, but if your team consists mostly of junior developers skilled in vanilla JavaScript and jQuery, judgment is key. You must decide between adopting Next.js, which requires a learning curve, or using Express.js, which aligns with their current abilities. A practical choice might be to select Express.js for faster delivery and plan to train the team on Next.js later, balancing immediate needs with future growth.&lt;/p&gt;
&lt;h4 id=&#34;microservices-or-monolith&#34;&gt;Microservices or Monolith&lt;/h4&gt;
&lt;p&gt;Splitting a legacy app into microservices allows independent deployments and improved scaling, but with a team of five developers—only two familiar with Docker—judgment favors a monolith. A monolith is simpler to manage with limited operational skills, keeps latency lower by avoiding network calls between services, and reduces code complexity since there’s no need to coordinate multiple interdependent components. Pushing microservices could overburden the team, introducing challenges like Kubernetes configuration they can’t handle, along with higher latency from service-to-service communication and increased complexity in debugging distributed logic.&lt;/p&gt;
&lt;h4 id=&#34;testing-depth&#34;&gt;Testing Depth&lt;/h4&gt;
&lt;p&gt;Deciding between unit tests for every function or integration tests for major workflows depends on team capability. If your team excels at test-driven development, you might aim for 80%+ unit test coverage to catch edge cases early. However, if many struggle with writing mocks, judgment favors integration tests—covering fewer scenarios with broader scope—to suit their skills, even if it means some unit-level issues might slip through.&lt;/p&gt;
&lt;h4 id=&#34;legacy-code-overhaul&#34;&gt;Legacy Code Overhaul&lt;/h4&gt;
&lt;p&gt;Faced with a tangled PHP codebase—tightly coupled and untested—a full rewrite in Go might seem appealing, but judgment considers the team: one developer knows Go, while three are experienced in PHP. Rewriting would delay progress significantly, so a better approach is incremental refactoring—adding tests and decoupling modules—leveraging their PHP expertise while gradually improving the code’s structure.&lt;/p&gt;
&lt;h4 id=&#34;caching-strategy&#34;&gt;Caching Strategy&lt;/h4&gt;
&lt;p&gt;Redis could accelerate slow database queries for your API, but if only one busy team member has experience with it, judgment favors a simpler in-memory cache, like a Kotlin HashMap. Though less efficient than Redis, it’s faster for the team to implement, meeting current needs without overtaxing their limited expertise, while leaving space to transition to a more robust solution later.&lt;/p&gt;
&lt;h4 id=&#34;deadline-driven-shortcuts&#34;&gt;Deadline-Driven Shortcuts&lt;/h4&gt;
&lt;p&gt;The sprint’s closing, and the user authentication module still relies on a temporary database table. Do you leave it as-is to meet the deadline, or push the sprint to migrate it to a permanent schema? If your team has a strong track record of addressing technical debt post-launch, judgment opts for shipping now, confident they’ll refactor later. With a less reliable team, delaying is safer—otherwise, the makeshift table lingers, risking data inconsistencies down the line.&lt;/p&gt;
&lt;h3 id=&#34;improving-judgement&#34;&gt;Improving judgement&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1. Analyze Past Decisions with Metrics&lt;/strong&gt;&lt;br&gt;
After a project, measure the results of your choices. For example, if you chose a monolith over microservices, track deployment frequency (once daily vs. hourly) and bug rates (5% higher due to coupling) using tools like Jira or Sentry. Numbers make the impact clear, like a 20% slowdown from skipping Redis caching, helping you learn what works. Review quarterly.&lt;br&gt;
&lt;strong&gt;2. Shadow the Masters&lt;/strong&gt;&lt;br&gt;
Work with an experienced developer on a complex task, such as optimizing a query, and study their reasoning—like why they picked a JOIN over a subquery. You’ll pick up their decision-making process which builds your own judgment.&lt;br&gt;
&lt;strong&gt;3. Use Experimental Builds for Data&lt;/strong&gt;&lt;br&gt;
Build a small system twice—like a CRUD API with Flask and then Spring Boot—and compare build time (4 hours vs. 8 hours) and scalability (100 requests/second vs. 300 requests/second). Testing both options gives you concrete data, so you can compare metrics, not feelings.&lt;br&gt;
&lt;strong&gt;4. Formalize Judgment with Decision Criteria&lt;/strong&gt;&lt;br&gt;
Before deciding, like choosing PostgreSQL over MongoDB, list factors—schema structure, team experience (SQL: 80%, NoSQL: 20%), query needs (OLTP vs. OLAP)—and score them, then check results later with post-mortems. A clear score (Postgres: 85, Mongo: 60) helps you make reasoned choices.&lt;br&gt;
&lt;strong&gt;5. Broaden Technical Exposure&lt;/strong&gt;&lt;br&gt;
Learn new tools on a regular basis, such as Kafka for event streams, and build a simple pipeline (1000 messages, 500 messages/second throughput) to understand its trade-offs compared to options like RabbitMQ. Knowing more tools helps you make informed decisions.&lt;br&gt;
&lt;strong&gt;6. Know Your Team&lt;/strong&gt;&lt;br&gt;
Before starting a project, document each team member’s experience with key technologies and review how those skills align with the work, such as a backend-focused sprint requiring strong database or API expertise. After, check metrics like code review turnaround, feature delivery velocity, or tech debt tickets to see if gaps affected progress. Recognizing that skill gaps bogged down tasks helps you adjust future plans, like pairing novices with experts.&lt;br&gt;
&lt;strong&gt;7. Iterate Under Pressure&lt;/strong&gt;&lt;br&gt;
In a short deadline, like 48 hours, pick a solution—such as in-memory caching—deploy it, measure the result (latency from 200ms to 50ms), and adjust if needed (e.g., memory errors). Working under constraints teaches you to balance speed and quality effectively. Document the fix later.&lt;br&gt;
&lt;strong&gt;8. Read The Classics&lt;/strong&gt;&lt;br&gt;
Study foundational books like “Design Patterns” or “Refactoring” which offer proven principles to improve your decision-making skills. Apply a concept—such as replacing a switch statement with polymorphism—and measure the impact, like reducing cyclomatic complexity from 15 to 8. This gives you concrete data to understand why these practices matter&lt;/p&gt;
&lt;h3 id=&#34;software-development-is-a-gamble&#34;&gt;Software development is a gamble&lt;/h3&gt;
&lt;p&gt;Real-world decisions, like choosing in-memory caching over Redis, are gambles. Judgment grows when you make a choice and act, not when you overanalyze. Software is never truly “done”—it’s a living system shaped by human decisions. Good judgment means accepting imperfection, understanding that every line of code balances the ideal against the practical. You won’t have all the information upfront, so select a caching strategy, deploy it, and refine it later. Focus on reviewing actual results, not theoretical possibilities, and avoid stalling in pursuit of perfection.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Notes from Docker Up &amp; Running</title>
      <link>https://lzag.dev/notes-from-docker-up-running/</link>
      <pubDate>Sat, 08 Mar 2025 22:02:13 +0000</pubDate>
      <guid>https://lzag.dev/notes-from-docker-up-running/</guid>
      <description>&lt;h2 id=&#34;docker-up--running&#34;&gt;Docker Up &amp;amp; Running&lt;/h2&gt;
&lt;p&gt;Recently I was taking a deep dive into the workings of Docker with the help of &lt;a href=&#34;https://www.amazon.ca/Docker-Shipping-Reliable-Containers-Production-dp-1098131827/dp/1098131827&#34; title=&#34;Docker: Up &amp;amp; Running&#34;&gt;Docker: Up &amp;amp; Running&lt;/a&gt;. These notes list useful commands and info for later use, covering ways to handle, check, and tune containers and images.&lt;/p&gt;
&lt;h2 id=&#34;monitoring-and-stats&#34;&gt;Monitoring and Stats&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;docker stats &amp;lt;container-name&amp;gt;&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This one shows a live feed of resource usage stats for running containers, like CPU percentage, memory use, and network I/O. It acts as a real-time performance checker in the terminal, highlighting resource demands or spikes—handy for keeping an eye on container health without extra tools. The output refreshes constantly to track trends, and pairing it with &lt;code&gt;glances&lt;/code&gt; gives a broader view of system and Docker stats together.&lt;/p&gt;</description>
      <content>&lt;h2 id=&#34;docker-up--running&#34;&gt;Docker Up &amp;amp; Running&lt;/h2&gt;
&lt;p&gt;Recently I was taking a deep dive into the workings of Docker with the help of &lt;a href=&#34;https://www.amazon.ca/Docker-Shipping-Reliable-Containers-Production-dp-1098131827/dp/1098131827&#34; title=&#34;Docker: Up &amp;amp; Running&#34;&gt;Docker: Up &amp;amp; Running&lt;/a&gt;. These notes list useful commands and info for later use, covering ways to handle, check, and tune containers and images.&lt;/p&gt;
&lt;h2 id=&#34;monitoring-and-stats&#34;&gt;Monitoring and Stats&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;docker stats &amp;lt;container-name&amp;gt;&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This one shows a live feed of resource usage stats for running containers, like CPU percentage, memory use, and network I/O. It acts as a real-time performance checker in the terminal, highlighting resource demands or spikes—handy for keeping an eye on container health without extra tools. The output refreshes constantly to track trends, and pairing it with &lt;code&gt;glances&lt;/code&gt; gives a broader view of system and Docker stats together.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;docker top &amp;lt;container-name&amp;gt;&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Running this lists active processes inside a container, similar to a &lt;code&gt;ps&lt;/code&gt; command on a standard server, showing details like PID, user, and the command in use. It’s a quick way to check what’s running inside, spotting any odd processes without needing to log into the container, offering a direct look at operations through the Docker API.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;docker system events&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
This command streams live events from the Docker daemon, such as container starts, stops, or &lt;code&gt;exec&lt;/code&gt; calls, providing a real-time log of system activity. It’s useful for tracking what’s happening or catching potential security issues, like unexpected &lt;code&gt;exec&lt;/code&gt; commands, by leaving it running in a terminal to watch events as they occur.&lt;/p&gt;
&lt;h2 id=&#34;searching-and-exploring-images&#34;&gt;Searching and Exploring Images&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;docker search &amp;lt;term&amp;gt;&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
This one queries Docker Hub or another registry for images matching the term, returning names, descriptions, star counts, and official status flags. It’s a simple way to browse available images without downloading, showing how the client reaches out through the daemon to external services, all from the terminal.&lt;/p&gt;
&lt;h2 id=&#34;inspecting-containers&#34;&gt;Inspecting Containers&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;docker container diff &amp;lt;container-name&amp;gt;&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
This command lists filesystem changes in a container since it started, marking files as &lt;code&gt;A&lt;/code&gt; (added), &lt;code&gt;C&lt;/code&gt; (changed), or &lt;code&gt;D&lt;/code&gt; (deleted)—like a change tracker. It shows what’s been altered, such as new logs or updated files, making it easy to debug filesystem effects without entering the container, pulling data straight from the daemon.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;cd /var/lib/docker/containers/&amp;lt;hash&amp;gt;&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Navigating to &lt;code&gt;/var/lib/docker/containers/&amp;lt;hash&amp;gt;&lt;/code&gt; (where &lt;code&gt;&amp;lt;hash&amp;gt;&lt;/code&gt; is a container’s ID from &lt;code&gt;docker ps -a&lt;/code&gt;) is a shell step, not a Docker command, reaching the host directory where the daemon keeps container data like logs and configs. It gives a raw look at a container’s files, letting you check logs or configs to troubleshoot issues, skipping container entry.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;docker inspect &amp;lt;container-name&amp;gt;&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
This command retrieves comprehensive details about a container, including environment variables, log driver configuration (e.g., blocking or non-blocking), and other attributes, output as a single JSON object. This facilitates an in-depth analysis of a container’s setup, proving valuable insights for troubleshooting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;docker service inspect &amp;lt;service-name&amp;gt; --pretty&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
This command provides detailed information about a Docker service, such as those managed in Swarm mode, formatted in a human-readable structure. This offers an efficient method to examine service configuration and scaling parameters, retrieving data directly from the daemon in a clear, accessible presentation without requiring additional interpretation.&lt;/p&gt;
&lt;h2 id=&#34;image-history-and-optimization&#34;&gt;Image History and Optimization&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;docker image history&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
This one lists all build layers of an image, with full commands, sizes, and timestamps, helping figure out why an image might be too big. It shows Docker’s layers stack up—deleting files doesn’t shrink them, but multi-stage builds can—making it clear how to trim images for less network strain.&lt;/p&gt;
&lt;h2 id=&#34;system-and-context-insights&#34;&gt;System and Context Insights&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;docker context list&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Running this spits out a list of all configured Docker contexts—like local or remote daemons you’ve set up to work with. It’s a quick way to see which server the CLI is talking to, keeping things straight when juggling multiple environments, all without digging through config files.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;docker system info&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
This command dumps a bunch of useful stats about the Docker setup, like how many containers are running, total images, and system details. It’s a handy snapshot to check the daemon’s workload or see what’s chewing up space, delivered straight to the terminal.&lt;/p&gt;
&lt;h2 id=&#34;daemon-configuration-and-metrics&#34;&gt;Daemon Configuration and Metrics&lt;/h2&gt;
&lt;h3 id=&#34;metrics-endpoint-config&#34;&gt;Metrics Endpoint Config&lt;/h3&gt;
&lt;p&gt;Adding &lt;code&gt;{&amp;quot;experimental&amp;quot;: true, &amp;quot;metrics-addr&amp;quot;: &amp;quot;0.0.0.0:9323&amp;quot;}&lt;/code&gt; to &lt;code&gt;/etc/docker/daemon.json&lt;/code&gt; sets the daemon to share metrics on port 9323, accessible via the API for detailed runtime data. Using Prometheus in a container with &lt;code&gt;--network host&lt;/code&gt;, you can pull stats like CPU or memory use, giving a close look at how the daemon manages container resources, useful for performance checks.&lt;/p&gt;
&lt;h3 id=&#34;logs&#34;&gt;Logs&lt;/h3&gt;
&lt;p&gt;To configure the maximum log size for Docker containers, you can use the &amp;ndash;log-opt flag with the max-size option when running a container. For example, to set the maximum log size to 10 megabytes, you would use the following command:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;docker run --log-opt max-size=10m my-app:latest&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Alternatively, you can configure the logging options in the docker-compose.yml file under the logging section for a service. Here is an example configuration:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    logging:
      options:
        max-size: &amp;quot;10m&amp;quot;
        max-file: &amp;quot;5&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To apply these settings globally, you can set the log-opts in the /etc/docker/daemon.json file. Here is an example configuration:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &amp;quot;log-driver&amp;quot;: &amp;quot;json-file&amp;quot;,
  &amp;quot;log-opts&amp;quot;: {
    &amp;quot;max-size&amp;quot;: &amp;quot;10m&amp;quot;,
    &amp;quot;max-file&amp;quot;: &amp;quot;5&amp;quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After making changes to the daemon.json file, you need to restart the Docker service for the changes to take effect.&lt;/p&gt;
&lt;p&gt;Existing containers will not automatically adopt the new logging configuration unless they are recreated. To update the logging driver for an existing container, you would need to stop and recreate the container with the desired logging options.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Coding challenge – HTML web server in PHP</title>
      <link>https://lzag.dev/coding-challenge-html-web-server-in-php/</link>
      <pubDate>Sun, 02 Mar 2025 16:58:44 +0000</pubDate>
      <guid>https://lzag.dev/coding-challenge-html-web-server-in-php/</guid>
      <description>&lt;p&gt;I wanted to learn a bit more about concurrency and network programming, so I built two TCP socket servers in PHP as a coding challenge. It’s a practical way to practice and get a grip on managing multiple connections at once. I’m not going for anything complex—just experimenting and picking up the basics step by step. The code can be found in my repository: &lt;a href=&#34;https://github.com/lzag/webserver&#34;&gt;github.com/lzag/webserver&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I found it quite useful to go through the &lt;a href=&#34;https://beej.us/guide/bgnet/html/split/&#34; title=&#34;Beej&amp;#39;s guide to network programming&#34;&gt;Beej&amp;rsquo;s guide to network programming&lt;/a&gt; to understand the inner workings of sockets programming and have a better idea of how the PHP wrappers work underneath. I added comments in the code linking to relevant sections of the guide. Another helpful resource is &lt;a href=&#34;https://www.amazon.ca/Mastering-Swoole-PHP-Performance-Concurrent/dp/1838134409&#34; title=&#34;Mastering Swoole PHP&#34;&gt;Mastering Swoole PHP&lt;/a&gt;, which explains concurrency concepts and includes some network programming examples.&lt;/p&gt;</description>
      <content>&lt;p&gt;I wanted to learn a bit more about concurrency and network programming, so I built two TCP socket servers in PHP as a coding challenge. It’s a practical way to practice and get a grip on managing multiple connections at once. I’m not going for anything complex—just experimenting and picking up the basics step by step. The code can be found in my repository: &lt;a href=&#34;https://github.com/lzag/webserver&#34;&gt;github.com/lzag/webserver&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I found it quite useful to go through the &lt;a href=&#34;https://beej.us/guide/bgnet/html/split/&#34; title=&#34;Beej&amp;#39;s guide to network programming&#34;&gt;Beej&amp;rsquo;s guide to network programming&lt;/a&gt; to understand the inner workings of sockets programming and have a better idea of how the PHP wrappers work underneath. I added comments in the code linking to relevant sections of the guide. Another helpful resource is &lt;a href=&#34;https://www.amazon.ca/Mastering-Swoole-PHP-Performance-Concurrent/dp/1838134409&#34; title=&#34;Mastering Swoole PHP&#34;&gt;Mastering Swoole PHP&lt;/a&gt;, which explains concurrency concepts and includes some network programming examples.&lt;/p&gt;
&lt;p&gt;To run the server I built a Docker image with the necessary extensions:&lt;br&gt;
&lt;a href=&#34;https://www.php.net/manual/en/book.parallel.php&#34; title=&#34;PHP Parallel extension&#34;&gt;PHP Parallel extension&lt;/a&gt;&lt;br&gt;
&lt;a href=&#34;https://www.php.net/manual/en/book.sockets.php&#34; title=&#34;PHP Sockets extension&#34;&gt;PHP Sockets extension&lt;/a&gt;&lt;br&gt;
The former contains PHP wrappers around socket system calls and the latter is to achieve parallel execution.&lt;/p&gt;
&lt;p&gt;I also added a Locust container to the composee file with some basic configuration that allows me to test the performance of the server.&lt;/p&gt;
&lt;p&gt;There are 2 versions of the server - single and multi-threaded.&lt;/p&gt;
&lt;h3 id=&#34;single-threaded-server&#34;&gt;Single threaded server&lt;/h3&gt;
&lt;p&gt;First, I’m building a non-blocking single threaded socket server in PHP. I’m setting up a TCP server that listens on port 9090, manages multiple client connections concurrently, and handles incoming data asynchronously. This setup lets me explore concurrency concepts like non-blocking I/O and signal handling, managing multiple clients without relying on threads or forks.&lt;/p&gt;
&lt;p&gt;I create a TCP socket with socket_create(), using IPv4 (AF_INET) and a streaming protocol (SOCK_STREAM).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$socket &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;socket_create&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;AF_INET&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;SOCK_STREAM&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I bind the socket to 0.0.0.0 on port 9090, allowing connections from any interface. Any error can be retrieved using a combination of socket_last_erorr and socket_strerorr.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;socket_bind&lt;/span&gt;($socket, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;0.0.0.0&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;9090&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;socket_bind() failed: reason: &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;socket_strerror&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;socket_last_error&lt;/span&gt;($socket)) &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;exit&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I set the socket to listen for incoming connections using socket_listen().&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;socket_listen&lt;/span&gt;($socket) &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;socket_listen() failed: reason: &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;socket_strerror&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;socket_last_error&lt;/span&gt;($socket)) &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;exit&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I switch the socket to non-blocking mode with socket_set_nonblock() so it doesn’t hang on I/O operations and maintain an array of clients to track all active client sockets.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;socket_set_nonblock&lt;/span&gt;($socket);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$clients &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [];
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I register a SIGINT signal handler with pcntl_signal() to shut down gracefully when I interrupt it (e.g., with Ctrl+C). Normally Docker compose would send SIGTERM, but I configure it to send SIGINT like the Linux command line.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;pcntl_signal&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;SIGINT&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; () &lt;span style=&#34;color:#66d9ef&#34;&gt;use&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;$running) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    $running &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Caught signal, shutting down...&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I run an infinite loop until I stop it:&lt;br&gt;
I check for new connections with socket_accept() and add them to my $clients array. I loop through connected clients, reading their data with socket_read() in non-blocking mode. I process their input, sending back an HTTP 200 OK response, and close their connection if they send &amp;ldquo;quit. I dispatch pending signals with pcntl_signal_dispatch() and add a tiny delay (usleep(1)) to avoid overloading the CPU.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; ($running) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; ($conn &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;socket_accept&lt;/span&gt;($socket)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; ($conn &lt;span style=&#34;color:#f92672&#34;&gt;!==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;New connection is accepted&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;socket_set_nonblock&lt;/span&gt;($conn);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            $clients[] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; $conn;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            $id &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;sizeof&lt;/span&gt;($clients) &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Connection #[&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;$id&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;] is connected&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;count&lt;/span&gt;($clients)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;foreach&lt;/span&gt; ($clients &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $id &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; $conn) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; ($input &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;socket_read&lt;/span&gt;($conn, &lt;span style=&#34;color:#ae81ff&#34;&gt;512&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                $input &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;trim&lt;/span&gt;($input);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Connection #[&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;$id&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;] input: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;$input\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                $ack &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;HTTP/1.0 200 OK&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\r\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Content-Length: 0&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\r\n\r\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#a6e22e&#34;&gt;socket_write&lt;/span&gt;($conn, $ack, &lt;span style=&#34;color:#a6e22e&#34;&gt;strlen&lt;/span&gt;($ack));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; ($input &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;quit&amp;#39;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;socket_close&lt;/span&gt;($conn);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#a6e22e&#34;&gt;unset&lt;/span&gt;($clients[$id]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                    &lt;span style=&#34;color:#66d9ef&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Connection #[&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;$id&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;] is disconnected&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;pcntl_signal_dispatch&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;usleep&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When I shut it down, I close all active client connections in $clients and terminate the listening socket.&lt;/p&gt;
&lt;h3 id=&#34;multi-threaded-server&#34;&gt;Multi-threaded server&lt;/h3&gt;
&lt;p&gt;In the second version I’ve upgraded my original non-blocking socket server to use multiple threads with PHP’s parallel extension. While the core setup (listening on port 9090, non-blocking I/O, and signal handling) remains similar, I’ve introduced significant changes to handle requests concurrently with a thread pool and processing of HTTP-like inputs.&lt;/p&gt;
&lt;p&gt;I create a thread pool with 10 threads using parallel\Runtime to process client requests in parallel, replacing the single-threaded approach of the first version.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$poolSize &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;10&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$pool &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; ($i &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; $i &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; $poolSize; $i&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    $pool[] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Runtime&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I define a closure to handle HTTP requests, parsing the method, path, and protocol, serving files from /var/www/html, and returning formatted HTTP responses (e.g., 200 OK, 404 Not Found), instead of just echoing input and sending a static 200 OK. Sleep is there to simulate processing time.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$processRequest &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; ($input)&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;string&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;sleep&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    $docRoot &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/var/www/html&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;@&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;list&lt;/span&gt;($method, $path, $protocol) &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;explode&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt;, $input, &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;isset&lt;/span&gt;($method) &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;isset&lt;/span&gt;($path) &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;isset&lt;/span&gt;($protocol)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;HTTP/1.1 400 Bad Request&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\r\n\r\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; ($path &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/&amp;#39;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $path &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; $docRoot &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;page.html&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $path &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; $docRoot &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt; $path;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;file_exists&lt;/span&gt;($path)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $response &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;HTTP/1.1 404 Not Found&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\r\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Requested path: &lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;$path\r\n\r\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $responseBody &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;file_get_contents&lt;/span&gt;($path);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $response &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;HTTP/1.1 200 OK&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\r\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $response &lt;span style=&#34;color:#f92672&#34;&gt;.=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Content-Type: text/html&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\r\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $response &lt;span style=&#34;color:#f92672&#34;&gt;.=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Content-Length: &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;strlen&lt;/span&gt;($responseBody);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $response &lt;span style=&#34;color:#f92672&#34;&gt;.=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\r\n\r\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $response &lt;span style=&#34;color:#f92672&#34;&gt;.=&lt;/span&gt; $responseBody;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $response &lt;span style=&#34;color:#f92672&#34;&gt;.=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\r\n\r\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; $response;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I limit the number of concurrent clients to 100, rejecting new connections with a 503 Service Unavailable response if the limit is reached, unlike the first version which accepted unlimited clients.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;count&lt;/span&gt;($clients) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    $refusal &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;HTTP/1.1 503 Service Unavailable&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\r\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Content-Length: 20&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\r\n\r\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;Server is full, sorry&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;socket_write&lt;/span&gt;($incoming_conn, $refusal, &lt;span style=&#34;color:#a6e22e&#34;&gt;strlen&lt;/span&gt;($refusal));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;socket_close&lt;/span&gt;($incoming_conn);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Refused a connection - client limit (100) reached&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Connection is accepted&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    $clients[] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; $incoming_conn;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I replace the simple polling loop with socket_select() to efficiently monitor multiple sockets (the server socket and clients) for readability, improving scalability over the first version’s manual checks.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$readfds &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;array_merge&lt;/span&gt;($clients, [$socket]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$writefds &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;NULL&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$errorfds &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$select &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;socket_select&lt;/span&gt;($readfds, $writefds, $errorfds, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1000&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I offload request processing to the thread pool by submitting tasks to the thread pool with run(), storing the results in an array, instead of handling requests inline like in the first version.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; ($input &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;socket_read&lt;/span&gt;($conn, &lt;span style=&#34;color:#ae81ff&#34;&gt;8192&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    $input &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;trim&lt;/span&gt;($input);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    $futures[$connkey] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; $pool[&lt;span style=&#34;color:#a6e22e&#34;&gt;array_rand&lt;/span&gt;($pool)]&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;run&lt;/span&gt;($processRequest, [$input]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I check completed futures in a separate loop, write their responses to clients, and close connections immediately after, unlike the first version where I kept connections open until a &amp;ldquo;quit&amp;rdquo; command.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;foreach&lt;/span&gt; ($futures &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; $key &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; $future) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; ($future&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;done&lt;/span&gt;()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $response &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; $future&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $conn &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; $clients[$key];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;echo&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Conn #[&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;$key&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;] writing response...&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;socket_write&lt;/span&gt;($conn, $response, &lt;span style=&#34;color:#a6e22e&#34;&gt;strlen&lt;/span&gt;($response));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;unset&lt;/span&gt;($futures[$key]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;socket_close&lt;/span&gt;($conn);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;unset&lt;/span&gt;($clients[$key]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I modify the main loop to continue running until both $running is false and all $futures are resolved, ensuring all threaded tasks complete before shutdown, unlike the first version which stopped immediately.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; ($running &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;empty&lt;/span&gt;($futures)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// ... (rest of the loop)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;These changes shift my server from a basic, single-threaded echo service to a multi-threaded, HTTP-capable server with connection limits and efficient I/O handling, leveraging parallelism for better performance.&lt;/p&gt;
&lt;p&gt;Testing in Locust shows the difference in peformance: the first version only can achieve 1 RPS vs the other close to 10 RPS. In real life we would avoid blocking the tread, but for this simple exercise that&amp;rsquo;s good enough.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Notes from  Grokking Concurrency</title>
      <link>https://lzag.dev/notes-from-grokking-concurrency/</link>
      <pubDate>Sat, 01 Mar 2025 14:05:16 +0000</pubDate>
      <guid>https://lzag.dev/notes-from-grokking-concurrency/</guid>
      <description>&lt;h2 id=&#34;introduction&#34;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;I wrote this summary of &lt;em&gt;&lt;a href=&#34;https://www.amazon.ca/Grokking-Concurrency-Kirill-Bobrov/dp/1633439771&#34;&gt;Grokking Concurrency&lt;/a&gt;&lt;/em&gt; to take notes and help me remember the main ideas about parallel programming. The book covers things like multitasking and synchronization with examples and explanations that I want to understand better. It’s a way for me to keep track of what I’m learning so I can use it later.&lt;/p&gt;
&lt;h2 id=&#34;sequential-programming-pros-and-cons&#34;&gt;Sequential Programming: Pros and Cons&lt;/h2&gt;
&lt;p&gt;Sequential programming refers to executing tasks one after another in a single-threaded, linear fashion.&lt;/p&gt;</description>
      <content>&lt;h2 id=&#34;introduction&#34;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;I wrote this summary of &lt;em&gt;&lt;a href=&#34;https://www.amazon.ca/Grokking-Concurrency-Kirill-Bobrov/dp/1633439771&#34;&gt;Grokking Concurrency&lt;/a&gt;&lt;/em&gt; to take notes and help me remember the main ideas about parallel programming. The book covers things like multitasking and synchronization with examples and explanations that I want to understand better. It’s a way for me to keep track of what I’m learning so I can use it later.&lt;/p&gt;
&lt;h2 id=&#34;sequential-programming-pros-and-cons&#34;&gt;Sequential Programming: Pros and Cons&lt;/h2&gt;
&lt;p&gt;Sequential programming refers to executing tasks one after another in a single-threaded, linear fashion.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Its simplicity makes it easy to design, implement, and debug. The straightforward flow eliminates the need to manage complex interactions between tasks.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Limited Scalability: Sequential programs can only scale vertically (e.g., by upgrading to a faster CPU), which has physical and economic limits. Horizontal scaling (adding more processors) isn’t possible without parallelism.&lt;/li&gt;
&lt;li&gt;Resource Inefficiency: System resources, such as CPU cores, memory, and I/O, are underutilized because tasks wait idly for others to complete, introducing significant overhead.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;transitioning-to-parallelism&#34;&gt;Transitioning to Parallelism&lt;/h2&gt;
&lt;p&gt;To process tasks independently, they must be inherently separable—i.e., their execution shouldn’t depend on the outcome of other tasks. While any parallel task can theoretically be executed sequentially, the reverse isn’t always true. Some tasks can be partially parallelized, but this introduces synchronization points—moments where parallel threads must coordinate. Synchronization adds overhead (e.g., locking mechanisms or waiting times), which can negate the performance benefits of parallelization if not carefully managed. In extreme cases, the overhead may outweigh the gains, making parallelization impractical.&lt;/p&gt;
&lt;h2 id=&#34;amdahls-law&#34;&gt;Amdahl’s Law&lt;/h2&gt;
&lt;p&gt;Amdahl’s Law quantifies the theoretical speedup of a program when parallelized. It states that the maximum speedup is limited by the fraction of the code that must remain sequential. Mathematically, if ( P ) is the proportion of a program that can be parallelized and ( N ) is the number of processors, the speedup ( S ) is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;S = 1 / ((1 - P) + (P / N))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Where:&lt;br&gt;
S = Speedup&lt;br&gt;
P = Fraction of program that can be parallelized&lt;br&gt;
N = Number of processors&lt;br&gt;
Thus, a parallel program’s performance is ultimately bottlenecked by its slowest sequential component. For example, if 20% of a program is sequential (1 - P = 0.2), the maximum speedup, even with infinite processors, is 5x.&lt;/p&gt;
&lt;h2 id=&#34;cpu-execution-cycle&#34;&gt;CPU Execution Cycle&lt;/h2&gt;
&lt;p&gt;Understanding how CPUs execute instructions is key to concurrency:&lt;br&gt;
&lt;strong&gt;Fetch:&lt;/strong&gt; The CPU retrieves an instruction from the cache (or memory, if not cached).&lt;br&gt;
&lt;strong&gt;Decode:&lt;/strong&gt; The instruction is interpreted by the control unit.&lt;br&gt;
&lt;strong&gt;Execute:&lt;/strong&gt; The Arithmetic Logic Unit (ALU) performs the computation or operation.&lt;br&gt;
&lt;strong&gt;Store:&lt;/strong&gt; Results are written back to cache or memory.&lt;br&gt;
Fetching from main memory is significantly slower than from cache (often by orders of magnitude), so modern CPUs use multiple cache levels (L1, L2, L3) to minimize latency and keep the processor busy. Efficient concurrency design leverages this by optimizing data locality and minimizing cache misses.&lt;/p&gt;
&lt;h3 id=&#34;cpus-vs-gpus&#34;&gt;CPUs vs. GPUs&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;CPUs:&lt;/strong&gt; Designed for general-purpose computing, CPUs use a MIMD (Multiple Instruction, Multiple Data) architecture, supporting a wide range of instructions with moderate parallelism (e.g., via multi-core designs). They excel at complex, sequential tasks.&lt;br&gt;
&lt;strong&gt;GPUs:&lt;/strong&gt; Built for parallel processing, GPUs use a SIMD (Single Instruction, Multiple Data) architecture, executing the same instruction across many data points simultaneously. They’re ideal for tasks like graphics rendering or matrix computations but are limited to a narrower instruction set.&lt;/p&gt;
&lt;h2 id=&#34;process-states&#34;&gt;Process States&lt;/h2&gt;
&lt;p&gt;A process transitions through distinct states during its lifecycle:&lt;br&gt;
&lt;strong&gt;Created:&lt;/strong&gt; The process is initialized in memory, allocated resources, and prepared for execution.&lt;br&gt;
&lt;strong&gt;Ready:&lt;/strong&gt; It resides in a queue, waiting for CPU assignment.&lt;br&gt;
&lt;strong&gt;Running:&lt;/strong&gt; The process is actively executing on the CPU.&lt;br&gt;
&lt;strong&gt;Terminated:&lt;/strong&gt; Execution completes, and the operating system frees associated resources (e.g., memory, file descriptors).&lt;/p&gt;
&lt;h2 id=&#34;operating-system-abstractions-posix-threads-pthreads&#34;&gt;Operating System Abstractions: POSIX Threads (Pthreads)&lt;/h2&gt;
&lt;p&gt;The OS provides abstractions to manage program execution:&lt;br&gt;
&lt;strong&gt;File Descriptors:&lt;/strong&gt; In Unix-like systems, these are handles to I/O resources (files, pipes, sockets). They enable safe, efficient interaction between processes and the system.&lt;br&gt;
&lt;strong&gt;Threads:&lt;/strong&gt; Lightweight units of execution within a process. Unlike processes, threads share the same memory address space but maintain their own stack and instruction stream. Introduced to reduce the overhead of inter-process communication (IPC), threads are scheduled independently by the OS.&lt;br&gt;
Advantages: Lower memory usage and faster context switching compared to processes.&lt;br&gt;
Disadvantages: Shared memory necessitates synchronization to prevent data corruption, adding complexity.&lt;/p&gt;
&lt;h2 id=&#34;inter-process-communication-ipc&#34;&gt;Inter-Process Communication (IPC)&lt;/h2&gt;
&lt;p&gt;IPC enables data exchange between processes. Common methods include:&lt;br&gt;
&lt;strong&gt;Shared Memory:&lt;/strong&gt; Processes access a common memory region. It’s fast but requires synchronization to avoid race conditions.&lt;br&gt;
&lt;strong&gt;Message Passing:&lt;/strong&gt; The OS manages communication channels, copying data between user and kernel space via system calls. This is slower than shared memory due to the overhead of data copying.&lt;br&gt;
&lt;strong&gt;Pipes:&lt;/strong&gt; A unidirectional communication channel implemented as a file descriptor. Pipes block until data is available, making them simple but limited. &lt;a href=&#34;https://www.youtube.com/watch?v=uHH7nHkgZ4w&#34;&gt;This&lt;/a&gt; is a fairly good explanation of the working of pipes.&lt;br&gt;
&lt;strong&gt;Message Queues:&lt;/strong&gt; Structured buffers for sending/receiving messages, offering more flexibility than pipes.&lt;br&gt;
&lt;strong&gt;Unix Domain Sockets:&lt;/strong&gt; Similar to network sockets but optimized for local communication, supporting both stream and datagram modes.&lt;/p&gt;
&lt;h2 id=&#34;multitasking&#34;&gt;Multitasking&lt;/h2&gt;
&lt;p&gt;Multitasking enables concurrent execution of multiple tasks, but it’s a runtime feature managed by the OS, not the hardware. Applications are classified as:&lt;br&gt;
&lt;strong&gt;CPU-Bound:&lt;/strong&gt; Limited by processing power (e.g., computations).&lt;br&gt;
&lt;strong&gt;I/O-Bound:&lt;/strong&gt; Limited by input/output operations (e.g., disk or network access).&lt;/p&gt;
&lt;h3 id=&#34;types-of-multitasking&#34;&gt;Types of multitasking:&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Preemptive:&lt;/strong&gt; The OS scheduler allocates CPU time slices to tasks, interrupting them as needed for fairness and responsiveness.&lt;br&gt;
&lt;strong&gt;Cooperative:&lt;/strong&gt; Tasks voluntarily yield control, relying on programmer discipline (less common in modern systems due to reliability issues).&lt;/p&gt;
&lt;h3 id=&#34;task-decomposition&#34;&gt;Task Decomposition&lt;/h3&gt;
&lt;p&gt;Task decomposition breaks an application into independent, parallelizable units based on functionality:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Identify separable tasks.&lt;/li&gt;
&lt;li&gt;Create a dependency graph to map task relationships.&lt;/li&gt;
&lt;li&gt;Apply techniques:&lt;br&gt;
&lt;strong&gt;Pipeline Pattern:&lt;/strong&gt; Tasks process data in stages, like an assembly line.&lt;br&gt;
&lt;strong&gt;Data Decomposition:&lt;/strong&gt; Split data into chunks processed independently.&lt;br&gt;
&lt;strong&gt;Fork/Join, Map/Reduce, Map Patterns:&lt;/strong&gt; Tasks execute autonomously with no side effects, then combine results.&lt;/li&gt;
&lt;li&gt;Determine granularity: Fine-grained decomposition increases parallelism but also communication overhead—a critical trade-off.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;synchronization-and-race-conditions&#34;&gt;Synchronization and Race Conditions&lt;/h2&gt;
&lt;p&gt;A race condition occurs when multiple tasks access a shared resource, and the outcome depends on execution order. Synchronization ensures orderly access:&lt;br&gt;
&lt;strong&gt;Mutexes:&lt;/strong&gt; Locks that only the owning thread can release, protecting critical sections.&lt;br&gt;
&lt;strong&gt;Semaphores:&lt;/strong&gt; Generalized locks with a counter, allowing multiple resource instances. They can be manipulated by different processes.&lt;br&gt;
&lt;strong&gt;Atomic Operations:&lt;/strong&gt; Operations (e.g., incrementing a variable) that complete indivisibly, invisible to other threads in a partial state. An atomic operation ensures a memory update is uninterrupted, preventing race conditions in concurrent environments&lt;/p&gt;
&lt;h2 id=&#34;deadlocks&#34;&gt;Deadlocks&lt;/h2&gt;
&lt;p&gt;Deadlocks arise when tasks wait indefinitely for resources held by each other. Solutions include:&lt;br&gt;
&lt;strong&gt;Arbitrator:&lt;/strong&gt; A central authority resolves conflicts.&lt;br&gt;
&lt;strong&gt;Resource Hierarchy:&lt;/strong&gt; Assigns an order to resource acquisition, preventing circular waits.&lt;/p&gt;
&lt;h2 id=&#34;what-i-liked-about-the-book&#34;&gt;What I Liked About the Book&lt;/h2&gt;
&lt;p&gt;Grokking Concurrency does a great job of making tricky ideas easy to understand with clear explanations and hands-on code examples. I really liked how the detailed code examples showed every concept in the book—they made things really clear for me. It teaches the basics of multitasking, keeping things in sync, and designing for parallel work, which are super important for today’s programming.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Setting up MySQL replication with Docker</title>
      <link>https://lzag.dev/setting-up-mysql-replication-with-docker/</link>
      <pubDate>Sat, 01 Mar 2025 02:41:39 +0000</pubDate>
      <guid>https://lzag.dev/setting-up-mysql-replication-with-docker/</guid>
      <description>&lt;h2 id=&#34;introduction&#34;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Setting up MySQL replication is a powerful way to enhance the reliability, scalability, and performance of your database system. In simple terms, MySQL replication is a process where data from one MySQL database server (known as the primary or master) is automatically copied to one or more additional servers (called replicas or slaves). This creates a live backup of your data, ensuring that if the primary server fails, a replica can step in with minimal downtime. Beyond redundancy, replication also allows you to distribute read-heavy workloads across multiple servers, improving efficiency and speed for applications that rely on quick data access.&lt;/p&gt;</description>
      <content>&lt;h2 id=&#34;introduction&#34;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Setting up MySQL replication is a powerful way to enhance the reliability, scalability, and performance of your database system. In simple terms, MySQL replication is a process where data from one MySQL database server (known as the primary or master) is automatically copied to one or more additional servers (called replicas or slaves). This creates a live backup of your data, ensuring that if the primary server fails, a replica can step in with minimal downtime. Beyond redundancy, replication also allows you to distribute read-heavy workloads across multiple servers, improving efficiency and speed for applications that rely on quick data access.&lt;/p&gt;
&lt;h2 id=&#34;importance-of-db-replication&#34;&gt;Importance of DB replication&lt;/h2&gt;
&lt;p&gt;Why is this important? In today’s world, where uptime and fast performance are non-negotiable, replication provides a safety net against hardware failures, network issues, or unexpected outages. It’s a cornerstone for businesses running critical applications—like e-commerce platforms, content management systems, or analytics tools—that can’t afford to lose data or leave users waiting. Plus, with a well-configured setup, you can scale your database infrastructure as your needs grow, all while keeping everything in sync. In this post, we’ll walk through the steps to set up MySQL replication, so you can unlock these benefits for your own projects.&lt;/p&gt;
&lt;h2 id=&#34;inner-works-of-mysql-replication&#34;&gt;Inner works of MySQL replication&lt;/h2&gt;
&lt;p&gt;MySQL replication works by copying and syncing data from a primary server (master) to one or more replica servers (slaves). The primary server logs all changes—like inserts, updates, or deletes—in a binary log. The replicas then read this log and apply those changes to their own databases in near real-time. A key component, the I/O thread, pulls the log data from the primary, while an SQL thread on the replica executes the updates locally. This setup keeps the data consistent across servers, enabling redundancy and load balancing.&lt;/p&gt;
&lt;h2 id=&#34;setting-up-replication-with-docker-compose&#34;&gt;Setting up replication with Docker Compose&lt;/h2&gt;
&lt;p&gt;The source and each replica must be configured with a unique ID (using the server_id system variable).&lt;br&gt;
The binlog_do_db setting in MySQL controls which databases are included in the binary log on the primary server during replication. When you specify a database with this option (e.g., binlog_do_db=my_database), only changes to that specific database—like table updates or inserts—are recorded in the binary log.&lt;br&gt;
On the replicata there are settings that can limit the statements to specifc dbs or tables:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;replicate-do-db: Tells replication SQL thread to restrict replication to specified database.&lt;/li&gt;
&lt;li&gt;replicate-do-table: Tells replication SQL thread to restrict replication to specified table.&lt;/li&gt;
&lt;li&gt;replicate-ignore-db: Tells replication SQL thread not to replicate to specified database.&lt;/li&gt;
&lt;li&gt;replicate-ignore-table: Tells replication SQL thread not to replicate to specified table.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In addition, each replica must be configured with information about the source&amp;rsquo;s host name, log file name, and position within that file.&lt;/p&gt;
&lt;p&gt;Here is a repo where I set up replication using Docker. Just by running &lt;code&gt;&amp;lt;code&amp;gt;docker compose up&lt;/code&gt;&lt;/code&gt; you can have a MySQL db with a replica for testing: &lt;a href=&#34;https://github.com/lzag/docker-mysql-master-slave&#34;&gt;github.com/lzag/docker-mysql-master-slave&lt;/a&gt;&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Thoughts on software architecture and decision-making</title>
      <link>https://lzag.dev/thoughts-on-software-architecture-and-decision-making/</link>
      <pubDate>Sat, 04 Jan 2025 14:05:17 +0000</pubDate>
      <guid>https://lzag.dev/thoughts-on-software-architecture-and-decision-making/</guid>
      <description>&lt;p&gt;Recently, I read the book &lt;a href=&#34;http://https://www.amazon.ca/Software-Architecture-Decision-Making-Leveraging-Leadership&#34; title=&#34;Software Architecture and Decision-Making&#34;&gt;Software Architecture and Decision-Making&lt;/a&gt; by Srinath Perera. It contains a lot of content that is available in other software architecture books but offers some unique angles on certain topics.&lt;/p&gt;
&lt;p&gt;I think the author is right to point out that you need to design deeply things that are hard to change, especially database schemas. For businesses, data is gold, and making changes to it comes with enormous risks. Therefore, changes are very rare and done only when absolutely necessary. The same applies to customer-facing APIs (or even internal APIs). These are usually contracts that other parts of the system adhere to, so changes to them come at a big cost. Facebook was famous for following the adage &amp;ldquo;move fast and break things,&amp;rdquo; but after they annoyed developers with constantly breaking APIs, they changed their attitude to &amp;ldquo;Move fast with stable infrastructure.&amp;rdquo;&lt;/p&gt;</description>
      <content>&lt;p&gt;Recently, I read the book &lt;a href=&#34;http://https://www.amazon.ca/Software-Architecture-Decision-Making-Leveraging-Leadership&#34; title=&#34;Software Architecture and Decision-Making&#34;&gt;Software Architecture and Decision-Making&lt;/a&gt; by Srinath Perera. It contains a lot of content that is available in other software architecture books but offers some unique angles on certain topics.&lt;/p&gt;
&lt;p&gt;I think the author is right to point out that you need to design deeply things that are hard to change, especially database schemas. For businesses, data is gold, and making changes to it comes with enormous risks. Therefore, changes are very rare and done only when absolutely necessary. The same applies to customer-facing APIs (or even internal APIs). These are usually contracts that other parts of the system adhere to, so changes to them come at a big cost. Facebook was famous for following the adage &amp;ldquo;move fast and break things,&amp;rdquo; but after they annoyed developers with constantly breaking APIs, they changed their attitude to &amp;ldquo;Move fast with stable infrastructure.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;The author also emphasizes the business environment where the architect operates. The skill level of the team is important when designing a system, as well as the required time to market.&lt;/p&gt;
&lt;p&gt;Often, naive implementations are the best to get the business going. The architect must be the person who, using their judgment, takes calculated risks and assumes responsibility for the outcome; otherwise, the whole team becomes blocked.&lt;/p&gt;
&lt;p&gt;Something that I haven&amp;rsquo;t encountered previously is a chapter discussing mental models explaining performance. I strongly believe that good mental models are essential in enabling good judgment when making software decisions, so seeing a chapter dedicated to mental models explaining performance was great. I&amp;rsquo;ve been spending a great deal of time trying to understand how computers work in order to build better and more performant systems.&lt;/p&gt;
&lt;p&gt;Another useful part is the one talking about the importance of testing and enforcing behavior from a management perspective. It&amp;rsquo;s important for a designer or manager to understand the team&amp;rsquo;s motivations and design processes in a way that makes it hard to avoid writing tests. Often in startups, this is omitted because they focus on developing as fast as possible. The role of a designer is to understand the importance of this and create incentives that make people want to write tests; otherwise, it might be easier to pass on the buck. Demanding excellence is also very important. Some companies don&amp;rsquo;t enforce excellence standards because they cause stress to the developers. We all make mistakes, so we shouldn&amp;rsquo;t blame the developers if something happens, but it&amp;rsquo;s important to enforce certain standards and strive to raise them for the team. It&amp;rsquo;s vital to remove anything that the developer doesn&amp;rsquo;t control from the equation, but they need to be held responsible if they don&amp;rsquo;t meet certain diligence standards. Otherwise, there is no incentive for the rest of the team to hold themselves to a higher standard.&lt;/p&gt;
&lt;p&gt;One of the big issues in tech is mindlessly copying architectures from popular companies like Uber or Netflix without considering the actual problems that should be solved, often resulting in building antipatterns like distributed monoliths. The book points this out in the microservices chapter and offers alternatives like repos-based teams. The same goes for building event-driven and asynchronous software. That requires developers to really understand how to write such systems, and without that knowledge, the systems produced might have additional complexity without the actual performance boosts.&lt;/p&gt;
&lt;p&gt;Overall, the book gives a very good overview of many leadership considerations when leading software projects without going too much into technical details, so I would highly recommend it.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>hello world</title>
      <link>https://lzag.dev/blog/hello-world/</link>
      <pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate>
      <guid>https://lzag.dev/blog/hello-world/</guid>
      <description>&lt;p&gt;New site, new stack. Ditched WordPress for Hugo + Cloudflare Pages.&lt;/p&gt;
&lt;p&gt;Posts are Markdown files, resume is built from LaTeX, everything is in git. No database, no PHP, no plugin updates at 2am.&lt;/p&gt;</description>
      <content>&lt;p&gt;New site, new stack. Ditched WordPress for Hugo + Cloudflare Pages.&lt;/p&gt;
&lt;p&gt;Posts are Markdown files, resume is built from LaTeX, everything is in git. No database, no PHP, no plugin updates at 2am.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Removing bloatware from Android phones</title>
      <link>https://lzag.dev/removing-bloatware-from-android-phones/</link>
      <pubDate>Sun, 20 Feb 2022 18:55:42 +0000</pubDate>
      <guid>https://lzag.dev/removing-bloatware-from-android-phones/</guid>
      <description>&lt;p&gt;Most Android phones come with many preinstalled apps that you might not ever use, but they do take up valuable space and clutter the interface. If you try to uninstall them the way you would any other apps you will find that it&amp;rsquo;s not possible. So is there a way to get rid of them?&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s actually not too complicted to uninstall them using the right tool which are available for every major operating system.&lt;/p&gt;</description>
      <content>&lt;p&gt;Most Android phones come with many preinstalled apps that you might not ever use, but they do take up valuable space and clutter the interface. If you try to uninstall them the way you would any other apps you will find that it&amp;rsquo;s not possible. So is there a way to get rid of them?&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s actually not too complicted to uninstall them using the right tool which are available for every major operating system.&lt;/p&gt;
&lt;p&gt;Keep in mind that while you can reinstall the packages if you change your mind later, but uninstalling them will wipe out all the application data and might sometimes interfere with correct functionning of other applications. Please make sure that you back up all your important data before starting to play around&lt;/p&gt;
&lt;p&gt;To install the necessary tools on Linux you can use the command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sudo apt update &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo apt install android-tools-adb android-tools-fastboot
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On Windows and MacOS you can use brew or choco package managers to install the tools.&lt;br&gt;
Windows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;choco install adb
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;MacOS:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;brew install android-platform-tools
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now that you have the tools on your computer you need to enable USB debugging on your phone. First, go to &lt;strong&gt;&lt;em&gt;Settings -&amp;gt; About Phone&lt;/em&gt;&lt;/strong&gt; and tap on the build number seven times until you unlock the developer mode. Then go to &lt;strong&gt;&lt;em&gt;Settings -&amp;gt; System -&amp;gt; Advanced -&amp;gt; Developer Options&lt;/em&gt;&lt;/strong&gt; and enable USB debugging.&lt;/p&gt;
&lt;p&gt;When you connect your phone to the PC via USB you should get a notification popup to confirm the incoming debugging connection from your PC. Your need to confirm it in order to proceed.&lt;/p&gt;
&lt;p&gt;To verify that the device is connected you can use the command:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;adb devices
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now your can list all the packages installed for your user.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;adb shell pm list packages --user &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can use the grep command to filter only specific types of packages, for example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;adb shell pm list packages --user &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; | grep google
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This would look for google pacakges. On Xiaomi phones you can search for &amp;lsquo;miui&amp;rsquo; which should return their preinstalled packages.&lt;br&gt;
If you would like to remove the package for a user (Gmail app in this case):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;adb shell pm uninstall -k --user &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; com.google.android.gm
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To find out the name of the package you can go the the play store page of an app and look at the URL. You should find the name of the package in the id parameter. This would be the link to the Gmail app:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;https://play.google.com/store/apps/details?id&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;com.google.android.gm
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To reinstall the app:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;adb shell cmd package install-existing com.google.android.gm
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can also choose to only disable the apple for the user:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;adb shell pm disable-user --user &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; com.google.android.gm
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To enable:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;adb shell pm enable --user &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; com.google.android.gm
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content>
    </item>
    
    <item>
      <title>Overview of PHP code quality tools</title>
      <link>https://lzag.dev/overview-of-php-code-quality-tools/</link>
      <pubDate>Sat, 18 Dec 2021 21:05:15 +0000</pubDate>
      <guid>https://lzag.dev/overview-of-php-code-quality-tools/</guid>
      <description>&lt;h3 id=&#34;general&#34;&gt;General&lt;/h3&gt;
&lt;p&gt;Below tools provide a comprehensive set of rules and analyse the code from different angles.&lt;/p&gt;
&lt;h6 id=&#34;phpstan&#34;&gt;&lt;a href=&#34;https://phpstan.org/&#34;&gt;PHPStan&lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;PHPStan focuses on finding errors in your code without actually running it. It catches whole classes of bugs even before you write tests for the code. It moves PHP closer to compiled languages in the sense that the correctness of each line of the code can be checked before you run the actual line.&lt;/p&gt;</description>
      <content>&lt;h3 id=&#34;general&#34;&gt;General&lt;/h3&gt;
&lt;p&gt;Below tools provide a comprehensive set of rules and analyse the code from different angles.&lt;/p&gt;
&lt;h6 id=&#34;phpstan&#34;&gt;&lt;a href=&#34;https://phpstan.org/&#34;&gt;PHPStan&lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;PHPStan focuses on finding errors in your code without actually running it. It catches whole classes of bugs even before you write tests for the code. It moves PHP closer to compiled languages in the sense that the correctness of each line of the code can be checked before you run the actual line.&lt;/p&gt;
&lt;h6 id=&#34;psalm&#34;&gt;&lt;a href=&#34;https://github.com/vimeo/psalm&#34;&gt;Psalm&lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;Very similar to PHPStan. Also can specify levels and there is probably a lot of overlap in terms of analysis, but could also be a useful tool.&lt;/p&gt;
&lt;h6 id=&#34;php-mess-detector&#34;&gt;&lt;a href=&#34;https://phpmd.org/&#34;&gt;PHP Mess Detector&lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;It is a spin-off project of PHP Depend and aims to be a PHP equivalent of the well known Java tool PMD. PHPMD can be seen as an user friendly and easy to configure frontend for the raw metrics measured by PHP Depend.&lt;/p&gt;
&lt;h6 id=&#34;phpcodesniffer&#34;&gt;&lt;a href=&#34;https://github.com/squizlabs/PHP_CodeSniffer&#34;&gt;PHPCodeSniffer&lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;Detects violations of the defined coding standards and helps to keep the code base clean and easy to read.&lt;/p&gt;
&lt;h6 id=&#34;phpmetrics&#34;&gt;&lt;a href=&#34;https://github.com/phpmetrics/PhpMetrics&#34;&gt;PHPMetrics&lt;/a&gt;&lt;/h6&gt;
&lt;p dir=&#34;auto&#34;&gt;
  PhpMetrics provides metrics about PHP project and classes, with beautiful and readable HTML report.
&lt;/p&gt;
&lt;h6 id=&#34;php-insights&#34;&gt;&lt;a href=&#34;https://phpinsights.com/&#34;&gt;PHP Insights&lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;The perfect starting point to analyse the code quality of your PHP projects.&lt;/p&gt;
&lt;h6 id=&#34;exakat&#34;&gt;&lt;a href=&#34;https://github.com/exakat/exakat&#34;&gt;Exakat&lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;Exakat looks very promising and comprehensive, but the biggest drawback is buggy documentation and small community. Generates reports in various formats, including HTML and analyses many areas like security or compatibility with specific versions of PHP.&lt;/p&gt;
&lt;h3 id=&#34;specialised&#34;&gt;Specialised&lt;/h3&gt;
&lt;p&gt;Below deal with specific areas of code quality.&lt;/p&gt;
&lt;h6 id=&#34;php-copypaste-detector-phpcpd&#34;&gt;&lt;a href=&#34;https://github.com/sebastianbergmann/phpcpd&#34;&gt;PHP Copy/Paste Detector (PHPCPD)&lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;phpcpd is a Copy/Paste Detector (CPD) for PHP code. Does what the name says. It discovers where simple copy paste was used or a duplicate code. Makes it possible to see where it can be wrapped up into a function.&lt;/p&gt;
&lt;h6 id=&#34;phpmnd-php-magic-number-detector&#34;&gt;&lt;a href=&#34;https://github.com/povils/phpmnd&#34;&gt;PHPMND (PHP Magic Number Detector)&lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;This tools is pretty specific: it can help you to find magic numbers your code.&lt;/p&gt;
&lt;h6 id=&#34;enlightn-security-checker&#34;&gt;&lt;a href=&#34;https://github.com/enlightn/security-checker&#34;&gt;Enlightn Security Checker&lt;/a&gt;&lt;/h6&gt;
&lt;h6 id=&#34;php-assumptions&#34;&gt;&lt;a href=&#34;https://github.com/rskuipers/php-assumptions&#34;&gt;PHP Assumptions&lt;/a&gt;&lt;/h6&gt;
&lt;h6 dir=&#34;auto&#34;&gt;
  &lt;a href=&#34;https://github.com/bmitch/churn-php&#34;&gt;churn-php&lt;/a&gt;
&lt;/h6&gt;
&lt;p dir=&#34;auto&#34;&gt;
  Helps discover good candidates for refactoring by finding classes that change often.
&lt;/p&gt;
&lt;h3 id=&#34;testing&#34;&gt;Testing&lt;/h3&gt;
&lt;p&gt;These tools test the code base or improve the quality of written tests&lt;/p&gt;
&lt;h6 id=&#34;phpunit&#34;&gt;&lt;a href=&#34;https://phpunit.de/&#34;&gt;PHPUnit&lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;PHPUnit is a programmer-oriented testing framework for PHP.&lt;/p&gt;
&lt;h6 id=&#34;infectionphp&#34;&gt;&lt;a href=&#34;https://infection.github.io/&#34;&gt;InfectionPHP&lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;PHP Mutation Testing Framework&lt;/p&gt;
&lt;h3 dir=&#34;auto&#34;&gt;
  Others
&lt;/h3&gt;
&lt;h6 id=&#34;rector&#34;&gt;&lt;a href=&#34;https://getrector.org/&#34;&gt;Rector&lt;/a&gt;&lt;/h6&gt;
&lt;p&gt;Rector is a CLI tool written in PHP. It can instantly upgrade old PHP code and handle automated refactorings. It&amp;rsquo;s fast and precise - changes 5000 files under minute.&lt;/p&gt;
&lt;h6 dir=&#34;auto&#34;&gt;
  &lt;a href=&#34;https://www.sonarlint.org/&#34;&gt;SonarLint&lt;/a&gt;
&lt;/h6&gt;
&lt;h6 id=&#34;deptrac&#34;&gt;&lt;a href=&#34;https://github.com/qossmic/deptrac&#34;&gt;Deptrac&lt;/a&gt;&lt;/h6&gt;
&lt;h6 id=&#34;php-parallel-lint&#34;&gt;&lt;a href=&#34;https://github.com/php-parallel-lint/PHP-Parallel-Lint&#34;&gt;PHP Parallel Lint&lt;/a&gt;&lt;/h6&gt;
&lt;h6 id=&#34;phan&#34;&gt;&lt;a href=&#34;https://github.com/phan/phan&#34;&gt;Phan&lt;/a&gt;&lt;/h6&gt;
&lt;h6 id=&#34;phplint&#34;&gt;&lt;a href=&#34;https://github.com/overtrue/phplint&#34;&gt;PHPLint&lt;/a&gt;&lt;/h6&gt;
&lt;h6 id=&#34;dephpend&#34;&gt;&lt;a href=&#34;https://dephpend.com/&#34;&gt;dePHPend&lt;/a&gt;&lt;/h6&gt;
&lt;h6 dir=&#34;auto&#34;&gt;
  &lt;a href=&#34;https://github.com/wapmorgan/PhpDeprecationDetector&#34;&gt;PhpDeprecationDetector&lt;/a&gt;
&lt;/h6&gt;
&lt;p dir=&#34;auto&#34;&gt;
  Analyser of PHP code to search usages of deprecated functionality in newer interpreter versions - deprecations detector. Doesn&#39;t support PHP 8.0 yet.
&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>How does browser fingerprinting work?</title>
      <link>https://lzag.dev/how-does-browser-fingerprinting-work/</link>
      <pubDate>Tue, 16 Mar 2021 01:46:53 +0000</pubDate>
      <guid>https://lzag.dev/how-does-browser-fingerprinting-work/</guid>
      <description>&lt;h6 id=&#34;what-is-browser-fingerprinting&#34;&gt;What is browser fingerprinting?&lt;/h6&gt;
&lt;p&gt;Nowadays, most people are familiar with cookies - information stored in browsers that helps websites keep track of our settings and track our actions online. Many users know how to delete cookies or navigate in private mode hoping that this will grant them privacy and prevent companies from following their actions online.&lt;/p&gt;
&lt;p&gt;However, there is a technique that allows to track the user online without the need for cookies called browser fingerprinting. One might think that just based on some general system settings it would be hard to tell computers from one another, but the blend of our systems&amp;rsquo; settings makes them quite unique and it&amp;rsquo;s usually possible to tell them apart.&lt;/p&gt;</description>
      <content>&lt;h6 id=&#34;what-is-browser-fingerprinting&#34;&gt;What is browser fingerprinting?&lt;/h6&gt;
&lt;p&gt;Nowadays, most people are familiar with cookies - information stored in browsers that helps websites keep track of our settings and track our actions online. Many users know how to delete cookies or navigate in private mode hoping that this will grant them privacy and prevent companies from following their actions online.&lt;/p&gt;
&lt;p&gt;However, there is a technique that allows to track the user online without the need for cookies called browser fingerprinting. One might think that just based on some general system settings it would be hard to tell computers from one another, but the blend of our systems&amp;rsquo; settings makes them quite unique and it&amp;rsquo;s usually possible to tell them apart.&lt;/p&gt;
&lt;p&gt;Browsers automatically send many bits of information about a machine whenever they make a petition to the website server.  These technical non user-specific details usually help with communication between the browser and the server, such as serving a correct version of the website for specific browser. One of the most common pieces of information shared is the user agent. For example, user agent for a Windows 10 machine that uses Edge browser would looks something like: &amp;lsquo;&lt;span class=&#34;field-items&#34;&gt;&lt;span class=&#34;field-item even&#34;&gt;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.246&lt;/span&gt;&lt;/span&gt;&amp;rsquo;. This string identifies the operating system and the specific version of the browser. This information is automatically sent in the headers when a website is loaded.&lt;/p&gt;
&lt;p&gt;Other pieces of information that the browser can access are: screen size, timezone, system fonts, language, video and audio hardware available etc. Fingerprinting can also perform operations with audio or video hardware to create unique hashes. Using a protocol called WebRTC it can even be possible to discover user&amp;rsquo;s IP behind a VPN. Even though fingerprinting doesn&amp;rsquo;t contain any personally identifiable information, it allows to identify users browsing on different website without his knowledge or consent.&lt;/p&gt;
&lt;h6 id=&#34;can-browser-fingerprinting-be-prevented&#34;&gt;Can browser fingerprinting be prevented?&lt;/h6&gt;
&lt;p&gt;Most browsers can be configured to make fingerprinting much harder, but it&amp;rsquo;s hard to prevent it completely, since browsers might need access to many of the functions that make fingerprinting possible just to display websites correctly or user advanced functionalities like videochats.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re interested in fingerprinting you can check out: &lt;a href=&#34;https://coveryourtracks.eff.org/&#34;&gt;https://coveryourtracks.eff.org/&lt;/a&gt;. This website will tell you which techniques can be used to identify your browser and how unique your signature is. It&amp;rsquo;s quite eye-opening to see what information about your computer is available every time you access a website and how unique it is.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Difference between virtual machines and containers</title>
      <link>https://lzag.dev/difference-between-virtual-machines-and-containers/</link>
      <pubDate>Mon, 24 Aug 2020 14:29:43 +0000</pubDate>
      <guid>https://lzag.dev/difference-between-virtual-machines-and-containers/</guid>
      <description>&lt;h4 id=&#34;definition&#34;&gt;Definition&lt;/h4&gt;
&lt;p&gt;Virtualization and containerization might seek to achieve the same goals but they are different concepts.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;virtual machine&lt;/strong&gt; loads an OS on its own it can be any type of OS that you would like. The virtual machine has its own hard drive, network interfaces etc. that are then mapped to the physical hardware of your machine. So you can easily run a Windows or macOS machine on you Ubuntu if you&amp;rsquo;d like to. Virtualization however is different from &lt;strong&gt;emulation&lt;/strong&gt;, so you&amp;rsquo;re still limited to the hardware that you have available.&lt;/p&gt;</description>
      <content>&lt;h4 id=&#34;definition&#34;&gt;Definition&lt;/h4&gt;
&lt;p&gt;Virtualization and containerization might seek to achieve the same goals but they are different concepts.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;virtual machine&lt;/strong&gt; loads an OS on its own it can be any type of OS that you would like. The virtual machine has its own hard drive, network interfaces etc. that are then mapped to the physical hardware of your machine. So you can easily run a Windows or macOS machine on you Ubuntu if you&amp;rsquo;d like to. Virtualization however is different from &lt;strong&gt;emulation&lt;/strong&gt;, so you&amp;rsquo;re still limited to the hardware that you have available.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Containerization&lt;/strong&gt; is the process of isolating the application dependencies into a container. It uses kernel functionalities (like control groups and namespaces) that allow for resource isolation. Since you&amp;rsquo;re not running a full guest OS you won&amp;rsquo;t be able to run a Windows container on a Linux machine and vice versa. Docker didn&amp;rsquo;t invent the concept of containers, but is the most popular solution at the moment and when people are talking about containers they usually refer to this solution.&lt;/p&gt;
&lt;h4 id=&#34;software-required&#34;&gt;Software required&lt;/h4&gt;
&lt;p&gt;In order to run a virtual machine on your computer you need a &lt;strong&gt;hypervisor&lt;/strong&gt;. As per Wikipedia a hypervisor is computer software, firmware or hardware that creates and runs virtual machines. There are two different types of hypervisors: type-1 or native ones run directly on the computer hardware while type-2 or hosted ones run on the host OS as a process. Examples of hypervisors a Microsoft Hyper-V, VirtualBox or VMware Player.&lt;/p&gt;
&lt;p&gt;The docker containers on the other hand are executed with the Docker engine that runs on the host OS.&lt;/p&gt;
&lt;h4 id=&#34;resource-isolation&#34;&gt;Resource isolation&lt;/h4&gt;
&lt;p&gt;The virtual machines  operate within a sandbox, meaning that they are completely isolated and self-contained. The containers share the operating system while virtual machines run an OS on their own. Resource isolation is one of the advantages that virtual machines have over containers.&lt;/p&gt;
&lt;h4 id=&#34;resources-consumption&#34;&gt;Resources consumption&lt;/h4&gt;
&lt;p&gt;Most companies choose Docker over VMs because of much smaller resource footprint. Virtual machines might also need to have the resources assigned to them permanently while they&amp;rsquo;re operating, containers allow for more flexibility and optimization.&lt;/p&gt;
&lt;p&gt;There is also less redundancy when using containers - you don&amp;rsquo;t have to install the full guest operating system, just components that you need for your application.&lt;/p&gt;
&lt;p&gt;Each VM runs not just a full copy of an operating system, but a virtual copy of all the hardware that the operating system needs to run. The guest OS has its own memory management and virtual device drivers.  As a result containers are smaller that VMs and have superior startup times than virtual machines. In many cases you might double the number of applications run and the speed when using containers. However, this might not be true in all situations. Current technology of virtualization reduced the overhead of CPU and memory usage and in some configuration a virtual machine might run faster than a Docker container [&lt;a href=&#34;https://dominoweb.draco.res.ibm.com/reports/rc25482.pdf&#34;&gt;1&lt;/a&gt;]&lt;a href=&#34;https://dominoweb.draco.res.ibm.com/reports/rc25482.pdf&#34;&gt;1&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The biggest advantage of containers is that you can create a lightweight, portable and above all consistent operating environment when you&amp;rsquo;re developing, testing and deploying applications.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Coding trivia 1</title>
      <link>https://lzag.dev/coding-trivia-1/</link>
      <pubDate>Sun, 09 Aug 2020 00:27:29 +0000</pubDate>
      <guid>https://lzag.dev/coding-trivia-1/</guid>
      <description>&lt;h4 id=&#34;random-interesting-stuff-that-i-learned-last-week&#34;&gt;Random interesting stuff that I learned last week&lt;/h4&gt;
&lt;h4 id=&#34;er-diagrams&#34;&gt;ER diagrams&lt;/h4&gt;
&lt;p&gt;Entity Relationship diagrams are important concept in database design. They help to conceptualize the relationship between tables in a database. I was looking for a quick refresher on the ER diagrams. These two videos did the job: &lt;a href=&#34;https://www.youtube.com/watch?v=QpdhBUYk7Kk&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Part 1&lt;/a&gt; and &lt;a href=&#34;https://www.youtube.com/watch?v=-CuY5ADwn24&#34;&gt;Part 2&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&#34;unix-sockets&#34;&gt;Unix sockets&lt;/h4&gt;
&lt;p&gt;You know when you&amp;rsquo;re trying to connect to MySQL and an error shows up that the socket file is missing? That&amp;rsquo;s because in MySQL the socket connection is enabled by default on localhost. What are UNIX socket files? These are files that enable two application to communicate with each other on the same machine, without the need for networking.&lt;br&gt;
Here is an in-depth explanation of how they work:&lt;br&gt;
&lt;a href=&#34;http://www.techdeviancy.com/uds.html&#34;&gt;www.techdeviancy.com/uds.html&lt;/a&gt;&lt;br&gt;
And here&amp;rsquo;s a quick video explanation:&lt;br&gt;
&lt;a href=&#34;https://www.youtube.com/watch?v=uXve8WYiGts&#34;&gt;www.youtube.com/watch?v=uXve8WYiGts&lt;/a&gt;&lt;br&gt;
More on the details of sockets:&lt;br&gt;
&lt;a href=&#34;https://stackoverflow.com/questions/14919441/principle-of-unix-domain-socket-how-does-it-work&#34;&gt;stackoverflow.com/questions/14919441/principle-of-unix-domain-socket-how-does-it-work&lt;/a&gt;&lt;/p&gt;</description>
      <content>&lt;h4 id=&#34;random-interesting-stuff-that-i-learned-last-week&#34;&gt;Random interesting stuff that I learned last week&lt;/h4&gt;
&lt;h4 id=&#34;er-diagrams&#34;&gt;ER diagrams&lt;/h4&gt;
&lt;p&gt;Entity Relationship diagrams are important concept in database design. They help to conceptualize the relationship between tables in a database. I was looking for a quick refresher on the ER diagrams. These two videos did the job: &lt;a href=&#34;https://www.youtube.com/watch?v=QpdhBUYk7Kk&#34; target=&#34;_blank&#34; rel=&#34;noopener noreferrer&#34;&gt;Part 1&lt;/a&gt; and &lt;a href=&#34;https://www.youtube.com/watch?v=-CuY5ADwn24&#34;&gt;Part 2&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&#34;unix-sockets&#34;&gt;Unix sockets&lt;/h4&gt;
&lt;p&gt;You know when you&amp;rsquo;re trying to connect to MySQL and an error shows up that the socket file is missing? That&amp;rsquo;s because in MySQL the socket connection is enabled by default on localhost. What are UNIX socket files? These are files that enable two application to communicate with each other on the same machine, without the need for networking.&lt;br&gt;
Here is an in-depth explanation of how they work:&lt;br&gt;
&lt;a href=&#34;http://www.techdeviancy.com/uds.html&#34;&gt;www.techdeviancy.com/uds.html&lt;/a&gt;&lt;br&gt;
And here&amp;rsquo;s a quick video explanation:&lt;br&gt;
&lt;a href=&#34;https://www.youtube.com/watch?v=uXve8WYiGts&#34;&gt;www.youtube.com/watch?v=uXve8WYiGts&lt;/a&gt;&lt;br&gt;
More on the details of sockets:&lt;br&gt;
&lt;a href=&#34;https://stackoverflow.com/questions/14919441/principle-of-unix-domain-socket-how-does-it-work&#34;&gt;stackoverflow.com/questions/14919441/principle-of-unix-domain-socket-how-does-it-work&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&#34;the-sticky-bit&#34;&gt;The sticky bit&lt;/h4&gt;
&lt;p&gt;In some cases you might want to set a folder to be writable to the whole world, bu limit the scope of changes that every user could do to the files. Sticky bit limits the activity in the folder so that only the owners of the files the overall directory or root user can rename or delete . This means that users won&amp;rsquo;t be able to mess with other user&amp;rsquo;s files  and makes sense to set it in collaborative directories.&lt;/p&gt;
&lt;p&gt;Adding it is easy with:&lt;br&gt;
&lt;code&gt;chmod +t test;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Sticky_bit&#34;&gt;Wikipedia&lt;/a&gt; has a good explanation of the concept&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Creating a custom keyboard layout in Linux</title>
      <link>https://lzag.dev/creating-a-custom-keyboard-layout-in-linux/</link>
      <pubDate>Sun, 14 Jun 2020 16:38:15 +0000</pubDate>
      <guid>https://lzag.dev/creating-a-custom-keyboard-layout-in-linux/</guid>
      <description>&lt;p&gt;Sometimes you might want to modify the keyboard layout, for example to add non standard characters that you&amp;rsquo;re often using or if you&amp;rsquo;re writing in 2 different languages you might combine all their special characters in one keyboard layout. In my case I bought a keyboard that had the escape key mapped by default to the Home Page special key and I found irritating having to press the Fn key every time I needed to use the escape button.&lt;/p&gt;</description>
      <content>&lt;p&gt;Sometimes you might want to modify the keyboard layout, for example to add non standard characters that you&amp;rsquo;re often using or if you&amp;rsquo;re writing in 2 different languages you might combine all their special characters in one keyboard layout. In my case I bought a keyboard that had the escape key mapped by default to the Home Page special key and I found irritating having to press the Fn key every time I needed to use the escape button.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m using the Lubuntu Linux distribution that uses the X keyboard extension to enable multiple keyboard layouts.&lt;/p&gt;
&lt;p&gt;First, run the xev command and filter only the keyboard events for a clearer log:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;xev -event keyboard
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When pressing a key it shows the keycode that is being sent and also the symbol of they key that is interpreted. In my case, using the standard US layout, the key 180 maps to the X86HomePage. The xev tool logs both the KeyPress and KeyRelease event:&lt;figure class=&#34;wp-block-image size-large&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://lzag.dev/wordpress/wp-content/uploads/2020/06/image.png&#34; alt=&#34;&#34;&gt; &lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;With this information we can look for the key symbol that we can use to modify the layout. In the directory &amp;ldquo;/usr/share/X11/xkb/keycodes&amp;rdquo; we can find the mapping in the evdev file.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;cat /usr/share/X11/xkb/keycodes/evdev | grep 180
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The output shows that the keycode 180 is mapped to the symbol &lt;I180&gt;:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt; &amp;lt;I180&amp;gt; = 180;   // #define KEY_HOMEPAGE            172
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now, there is an easy way to map the key to the escape button:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;xmodmap -e &amp;#34;keycode 180 = Escape&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To make the changes persistent we can save the command in the ~/.Xmodmap file&lt;/p&gt;
&lt;p&gt;However, we than can also define the layout in the &amp;ldquo;/usr/share/X11/xkb/symbols&amp;rdquo; folder&lt;/p&gt;
&lt;p&gt;The easiest way to go about it is to copy the one of the files that you can build upon. For example the standard US layout and then remove the contents of the layout.&lt;/p&gt;
&lt;p&gt;One of the layouts marked with default will be the main one and it also can have other variants.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;default  partial alphanumeric_keys modifier_keys
xkb_symbols &amp;#34;basic&amp;#34; {

    name[Group1]= &amp;#34;Custom Keyboard Layout&amp;#34;;

    include &amp;#34;us(basic)&amp;#34;;
    include &amp;#34;eurosign(5)&amp;#34;;

    key &amp;lt;I180&amp;gt; {        [ Escape                        ]        };
};
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In the code we&amp;rsquo;re first importing the US layout, then adding the Euro sign on the 5 button and finally mapping the I180 key to the Escape button. We can save the file as &amp;ldquo;cust&amp;rdquo; for example.&lt;/p&gt;
&lt;p&gt;After we save the file we should enable the configuration in the rules folder base.lst file, otherwise the configuration will not be available.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re looking for the layout section and adding the name of the file with the description of the Layout:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;! layout
  cust           Custom keyboard layout
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And with this the layout should be one of the choices in the layout selector.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Setting up the fixture in PHPUnit</title>
      <link>https://lzag.dev/setting-up-the-fixture-in-phpunit/</link>
      <pubDate>Sat, 06 Jun 2020 18:17:50 +0000</pubDate>
      <guid>https://lzag.dev/setting-up-the-fixture-in-phpunit/</guid>
      <description>&lt;p&gt;When running our tests we should begin with a know stage, called the fixture, and for that we might need to initialize variables etc. And after we finish the tests we might have some cleaning up to do.&lt;/p&gt;
&lt;p&gt;If you need to have custom code run before you execute them. PHPUnit allows you to configure a specific file to be run before the test execution.&lt;/p&gt;
&lt;p&gt;This directive in your phpunit.xml configuration file will execute the bootstrap.php fiel before the tests: :&lt;/p&gt;</description>
      <content>&lt;p&gt;When running our tests we should begin with a know stage, called the fixture, and for that we might need to initialize variables etc. And after we finish the tests we might have some cleaning up to do.&lt;/p&gt;
&lt;p&gt;If you need to have custom code run before you execute them. PHPUnit allows you to configure a specific file to be run before the test execution.&lt;/p&gt;
&lt;p&gt;This directive in your phpunit.xml configuration file will execute the bootstrap.php fiel before the tests: :&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;lt;phpunit bootstrap=&amp;#34;tests/bootstrap.php&amp;#34; &amp;gt;
    &amp;lt;!-- ... --&amp;gt;
&amp;lt;/phpunit&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The bootstrap file can also be specified in the command line argument:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;phpunit --bootstrap bootstrap.php
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If every method is the class needs the same setup we might use provided functions to save ourselves from repeating the code.&lt;/p&gt;
&lt;p&gt;PHPUnit has setUp() and tearDown methods that run before and after every method. We might use these for example to initialize variables.&lt;/p&gt;
&lt;p&gt;Additionally, setUpBeforeClass() and tearDownAfterClass methods are being run before any method in the class has been executed and after all of them finish, respectively. These might be useful to setting up for example a database connection. However, one has to keep in mind that that when tests share a fixture and are not fully independent it makes them harder to debug.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Setting up WordPress development environment with Docker</title>
      <link>https://lzag.dev/setting-up-wordpress-development-environtment-with-docker/</link>
      <pubDate>Sun, 24 May 2020 23:48:50 +0000</pubDate>
      <guid>https://lzag.dev/setting-up-wordpress-development-environtment-with-docker/</guid>
      <description>&lt;p&gt;One of the biggest challenges in web development is to have a stable development environment and assuring that the website works when deploying on different servers.&lt;/p&gt;
&lt;p&gt;Docker helps us build isolated containers that give a this stable and predictable environment and saves many headaches when debugging our website. Thanks to the preconfigured images available on Docker Hub and the Docker compose it&amp;rsquo;s a breeze to set up a development environment.&lt;/p&gt;</description>
      <content>&lt;p&gt;One of the biggest challenges in web development is to have a stable development environment and assuring that the website works when deploying on different servers.&lt;/p&gt;
&lt;p&gt;Docker helps us build isolated containers that give a this stable and predictable environment and saves many headaches when debugging our website. Thanks to the preconfigured images available on Docker Hub and the Docker compose it&amp;rsquo;s a breeze to set up a development environment.&lt;/p&gt;
&lt;p&gt;This post assumes that you already have Docker and Docker compose isntalled on your system. On Linux Docker Compose doesn&amp;rsquo;t come installed with Docker so make sure you install it separately.&lt;/p&gt;
&lt;p&gt;Create a directory to put the docker compose file into and then paste the following into the docker-compose file:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;version: &amp;#39;3.3&amp;#39;

services:
   db:
     image: mysql:5.7
     # declaring the volumes so we have persistent data
     volumes:
       - wp_data:/var/lib/mysql
     restart: always
     environment:
       MYSQL_ROOT_PASSWORD: password
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD: wordpress

   wordpress:
     container_name: docker_wordpress_dev
     depends_on:
       - db
     # using the latest wordpress image
     # Docker will automatically look it up on Docker Hub
     image: wordpress:latest
     # binding the loopback port 8000 on the host to port 80 on the docker container
     ports:
       - &amp;#34;127.0.0.1:8000:80&amp;#34;
     restart: always
     environment:
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD: wordpress
       WORDPRESS_DB_NAME: wordpress
# we need to declare the volumes that we will use
volumes:
    wp_data:
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After running the &amp;ldquo;docker-compose up -d&amp;rdquo; command you should be able to access WordPress on by entering localhost:8000 in your browser. This way you should should be able to bring up the installation page of WordPress:&lt;figure class=&#34;wp-block-image size-large&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://lzag.dev/wordpress/wp-content/uploads/2020/05/wp_installation_screen.png&#34; alt=&#34;&#34;&gt; &lt;/figure&gt;&lt;/p&gt;
&lt;p&gt;After completing the setup steps you&amp;rsquo;re ready to start working with WordPress.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;d like to access the source code you can the following command, which will bring you to the main folder with your WordPress installation:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;docker exec -it docker_wordpress_dev bash
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The database volume persists after the container is stopped. You can check that running the command &amp;ldquo;docker-compose stop&amp;rdquo; to stop the containers. Whenever you reinitialize the container you will be able to pick up where you left, so whatever changes you mad to your WordPress installation in Docker will persist. Also the database will persist in the wp_data volume.&lt;/p&gt;
&lt;p&gt;Thanks to the persisting database volume, even if you remove the container you should be able to spin the WordPress site back up from the same image.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Downloading and installing custom Linux kernels</title>
      <link>https://lzag.dev/downloading-and-installing-custom-linux-kernels/</link>
      <pubDate>Tue, 19 May 2020 03:59:04 +0000</pubDate>
      <guid>https://lzag.dev/downloading-and-installing-custom-linux-kernels/</guid>
      <description>&lt;p&gt;You might want to install a different kernel to which you have supplied with your current version of Linux. This might be because another version might work better with your hardware.&lt;/p&gt;
&lt;p&gt;The Linux kernel is the core of the operating system that facilitates interaction between the hardware components and software.&lt;/p&gt;
&lt;p&gt;You can install a different version of the kernel with the apt command:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;sudo apt install linux-image
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The command will present you with several options and you need to type the specific one you would like to install.&lt;/p&gt;</description>
      <content>&lt;p&gt;You might want to install a different kernel to which you have supplied with your current version of Linux. This might be because another version might work better with your hardware.&lt;/p&gt;
&lt;p&gt;The Linux kernel is the core of the operating system that facilitates interaction between the hardware components and software.&lt;/p&gt;
&lt;p&gt;You can install a different version of the kernel with the apt command:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;sudo apt install linux-image
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The command will present you with several options and you need to type the specific one you would like to install.&lt;/p&gt;
&lt;p&gt;After installing the kernel you will be able to choose an additional option from the Linux boot menu.&lt;/p&gt;
&lt;p&gt;In many cases the version you would like to install is not available for install from the command line and you would need to compile is from the source.&lt;/p&gt;
&lt;p&gt;You need to go to to &lt;a href=&#34;https://kernel.ubuntu.com/~kernel-ppa/mainline/&#34;&gt;kernel.ubuntu.com/~kernel-ppa/mainline/&lt;/a&gt; to download the deb packages for the version you would like to try.&lt;/p&gt;
&lt;p&gt;After downloading the correct files for you version and architecture you need to install them with the following command and the reboot your system:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;sudo dpkg -i linux*.deb
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In order to be able to boot, you might need to sign the binaries, disable validation for the kernel binary or disable Secure Boot altogether in BIOS.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Setting custom PHP code sniffing rules</title>
      <link>https://lzag.dev/setting-custom-php-sniffing-rules/</link>
      <pubDate>Fri, 15 May 2020 02:03:58 +0000</pubDate>
      <guid>https://lzag.dev/setting-custom-php-sniffing-rules/</guid>
      <description>&lt;p&gt;Code sniffing is the practice of checking the code for compliance with some pre-determined standards. It&amp;rsquo;s very important to run your code through a sniffer because it helps with maintenance and ensures better code quality.&lt;/p&gt;
&lt;p&gt;The most common standards in PHP are PSR-0, PSR-1, PSR2 and PEAR. These rules might specify details such as depth and type of indentation, placement of braces, spacing, variables names etc. Depending on the standard, they might go into a lot of detail about how the structure of your code should look like.&lt;/p&gt;</description>
      <content>&lt;p&gt;Code sniffing is the practice of checking the code for compliance with some pre-determined standards. It&amp;rsquo;s very important to run your code through a sniffer because it helps with maintenance and ensures better code quality.&lt;/p&gt;
&lt;p&gt;The most common standards in PHP are PSR-0, PSR-1, PSR2 and PEAR. These rules might specify details such as depth and type of indentation, placement of braces, spacing, variables names etc. Depending on the standard, they might go into a lot of detail about how the structure of your code should look like.&lt;/p&gt;
&lt;p&gt;A popular tool for sniffing code in PHP is the PHP CodeSniffer (phpcs).&lt;/p&gt;
&lt;p&gt;When running it it you can add arguments that specify the folder with the files, ignored directories, coding standards etc. When working on different projects for different clients it might be tiresome to have to specify them differently for every sniff. Phpcs lets you create an archive that specify custom rules and arguments to use. Whenever you run phps it will first look into the current directory for the file and if found, apply the configuration.&lt;/p&gt;
&lt;p&gt;The file should be be in an xml format. The most useful options that you can specify are:&lt;/p&gt;
&lt;p&gt;The file tag adds files and folders to be sniffed.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;lt;file&amp;gt;file.php&amp;lt;/file&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &amp;ldquo;exclude-pattern&amp;rdquo; tag lets you exclude specific directories or files from sniffing:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;lt;exclude-pattern&amp;gt;*/tests/Core/*/*Test\.(js|css)$&amp;lt;/exclude-pattern&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &amp;ldquo;rule&amp;rdquo; tag lets you add ruleset by its name and also exclude specific rules if you would not like to apply them:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;lt;rule ref=&amp;#34;PEAR&amp;#34;&amp;gt;
   &amp;lt;exclude name=&amp;#34;PEAR.NamingConventions.ValidFunctionName&amp;#34;/&amp;gt;
&amp;lt;/rule&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The arg tag lets you specify arguments that should be passed to the phpcs executable, just like the arguments from the command line. The names and acceptable values are sames as on the CLI, with a couple of arguments not permitted to be used there.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt; &amp;lt;arg name=&amp;#34;basepath&amp;#34; value=&amp;#34;.&amp;#34;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Using custom ruleset files is very useful when you would like to track the quality of your code and your projects must conform to different standard. Defining them before starting a project will surely help you produce cleaner code.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>How to deploy a React app in a subdirectory on a WordPress site</title>
      <link>https://lzag.dev/how-to-deploy-a-react-app-in-a-subdirectory-on-a-wordpress-site/</link>
      <pubDate>Thu, 14 May 2020 11:10:45 +0000</pubDate>
      <guid>https://lzag.dev/how-to-deploy-a-react-app-in-a-subdirectory-on-a-wordpress-site/</guid>
      <description>&lt;p&gt;The case here is pretty specific, but maybe somebody will be in the situation like me. I have a WordPress site and I wanted to deploy a React app to include in my portfolio and serve it from a subdirectory.&lt;/p&gt;
&lt;p&gt;When deploying a React app in a website subdirectory there are some hurdles to overcome. First of all, WordPress redirects most requests to the main index.php file, so when you&amp;rsquo;re trying to load the app the server might direct the request there and you will get an error. To resolve this you need to edit the .htaccess file so that the requests are correctly routed. Below is the code that you should add in your access file , before the rewrite conditions of WordPress:&lt;/p&gt;</description>
      <content>&lt;p&gt;The case here is pretty specific, but maybe somebody will be in the situation like me. I have a WordPress site and I wanted to deploy a React app to include in my portfolio and serve it from a subdirectory.&lt;/p&gt;
&lt;p&gt;When deploying a React app in a website subdirectory there are some hurdles to overcome. First of all, WordPress redirects most requests to the main index.php file, so when you&amp;rsquo;re trying to load the app the server might direct the request there and you will get an error. To resolve this you need to edit the .htaccess file so that the requests are correctly routed. Below is the code that you should add in your access file , before the rewrite conditions of WordPress:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;RewriteCond %{REQUEST_URI} ^/subdirectory/.*
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^.*$ /subdirectory/index.html [L]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If the request URL will start wit the subdirectory and is not an existing file (RewriteCond rules), the request will be routed to the main index file (RewriteRule). The [L] flag tells the server not to process any more rules - otherwise it will execute the following ones as well.&lt;/p&gt;
&lt;p&gt;Secondly, the React Router has a handy &amp;ldquo;basename&amp;rdquo; attribute that specifies that base of the URLs.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;lt;Router basename=&amp;#34;/subdirectory&amp;#34; &amp;gt;                
  &amp;lt;Route path=&amp;#34;/my-account&amp;#34; component={Form} /&amp;gt;
 &amp;lt;Route path=&amp;#34;/about&amp;#34; component={About} /&amp;gt;
&amp;lt;/Router&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If we don&amp;rsquo;t include the attribute the paths would be relative to the main directory and would break. With the baseame, they will all include the base path.&lt;/p&gt;
&lt;p&gt;And that&amp;rsquo;s pretty much it. With those 2 simple tweaks you can make your React app work from a subdirectory on a WordPress site.&lt;/p&gt;
&lt;p&gt;Under the URL &lt;a href=&#34;https://lzag.dev/goodreads-app&#34;&gt;https://lzag.dev/goodreads-app&lt;/a&gt; you can find a React app that I built.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>What are .htaccess files?</title>
      <link>https://lzag.dev/what-are-htaccess-files/</link>
      <pubDate>Wed, 13 May 2020 18:39:11 +0000</pubDate>
      <guid>https://lzag.dev/what-are-htaccess-files/</guid>
      <description>&lt;p&gt;If you were working with WordPress you probably noticed that the installation includes a .htaccess file in its main directory.&lt;/p&gt;
&lt;p&gt;Whate are these files?&lt;/p&gt;
&lt;p&gt;The .htaccess file set specific directives for the server that should be executed when an Apache server is handling requests. In the default case of WordPress it makes the server point most of the requests to the main index.php file, which then loads all the necessary scripts. The server needs to be configured to look for and honor the directives included there. That can be achieved using the AllowOverride directive that is specified by by directory. Access files allow you to set different directives per directory without the need to touch the main server configuration.&lt;/p&gt;</description>
      <content>&lt;p&gt;If you were working with WordPress you probably noticed that the installation includes a .htaccess file in its main directory.&lt;/p&gt;
&lt;p&gt;Whate are these files?&lt;/p&gt;
&lt;p&gt;The .htaccess file set specific directives for the server that should be executed when an Apache server is handling requests. In the default case of WordPress it makes the server point most of the requests to the main index.php file, which then loads all the necessary scripts. The server needs to be configured to look for and honor the directives included there. That can be achieved using the AllowOverride directive that is specified by by directory. Access files allow you to set different directives per directory without the need to touch the main server configuration.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;lt;Directory &amp;#34;/path/from/where/the/files/are/served&amp;#34;&amp;gt;
    AllowOverride All
&amp;lt;/Directory&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In this case the server will include all the directives that are premitted to be set there.&lt;/p&gt;
&lt;p&gt;Other options are:&lt;/p&gt;
&lt;ul class=&#34;wp-block-list&#34;&gt;
  &lt;li&gt;
    &#34;None&#34; - the server will not even look for .htaccess files
  &lt;/li&gt;
  &lt;li&gt;
    directive type - one or more of the directive groupings such as &#34;AuthConfig&#34; or &#34;FileInfo&#34;
  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The configuration is applied to the directory where the access file is found and all subdirectories. However, subdirectories might contain their own access file that override configuration from higher up, because it&amp;rsquo;s being applied in the order that the files are found.&lt;/p&gt;
&lt;p&gt;The default name of the access files is .htaccess, but this can be changes to something else using the AccessFileName directive:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;AccessFileName &amp;#34;.config&amp;#34;
&lt;/code&gt;&lt;/pre&gt;</content>
    </item>
    
    <item>
      <title>Docker volumes vs bind mounts</title>
      <link>https://lzag.dev/docker-volumes-vs-bind-mounts/</link>
      <pubDate>Tue, 12 May 2020 20:14:12 +0000</pubDate>
      <guid>https://lzag.dev/docker-volumes-vs-bind-mounts/</guid>
      <description>&lt;p&gt;By default a docker container won&amp;rsquo;t store any persistent data, so anything that we write to its writable layer won&amp;rsquo;t be available once we stop it.When running Docker containers we might be interested in persisting certain data. This can be achieved by using either volumes or bind mounts. Below I&amp;rsquo;m describing main differences between the two.&lt;/p&gt;
&lt;p&gt;Bind mounts are directories on the host filesystem mounted onto a Docker container. These can be modified outside of Docker.&lt;/p&gt;</description>
      <content>&lt;p&gt;By default a docker container won&amp;rsquo;t store any persistent data, so anything that we write to its writable layer won&amp;rsquo;t be available once we stop it.When running Docker containers we might be interested in persisting certain data. This can be achieved by using either volumes or bind mounts. Below I&amp;rsquo;m describing main differences between the two.&lt;/p&gt;
&lt;p&gt;Bind mounts are directories on the host filesystem mounted onto a Docker container. These can be modified outside of Docker.&lt;/p&gt;
&lt;p&gt;Volumes on the other hand are managed by Docker only and you can use Docker cli commands to manage them directly. They are a preferred way to persist data.&lt;/p&gt;
&lt;p&gt;There are named volumes created by the &amp;ldquo;volume&amp;rdquo; command that can be referenced by their name and there are volumes that are created by the VOLUME instruction from the docker files. The latter are identified by a hash and stored in the same Docker volumes folder.&lt;/p&gt;
&lt;p&gt;When mounting volumes you specify the name instead of the paths you would like to mount. Volumes don&amp;rsquo;t increase the size of docker containers. If you would like to keep your own directory structure you should use bind mounts. Volumes make your project more portable, because with bind mounts you would need to rebuild your directory structure when migrating and you must ensure that all the security settings like user permissions are setup correctly.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Different ways of running PHP on an Apache server</title>
      <link>https://lzag.dev/different-ways-of-running-php-on-an-apache-server/</link>
      <pubDate>Tue, 12 May 2020 00:24:59 +0000</pubDate>
      <guid>https://lzag.dev/different-ways-of-running-php-on-an-apache-server/</guid>
      <description>&lt;p&gt;When the server receives a request from the user it can serve the file directly, but sometimes it needs to translate the code using the appropriate application. Otherwise it would be showing the users the source code of the website. A server like Apache needs to be configured to handle specific content with an appropriate application like PHP.&lt;/p&gt;
&lt;p&gt;A common set of rules of passing the information between the server and the application is the Common Gateway Interface. This standard lets programmers write applications in different languages that can plug into the server and interact with it. The interface guarantees consistency of data passed between the server and the application. An alternative to a CGI application is Microsoft&amp;rsquo;s Active Server Page (ASP).&lt;/p&gt;</description>
      <content>&lt;p&gt;When the server receives a request from the user it can serve the file directly, but sometimes it needs to translate the code using the appropriate application. Otherwise it would be showing the users the source code of the website. A server like Apache needs to be configured to handle specific content with an appropriate application like PHP.&lt;/p&gt;
&lt;p&gt;A common set of rules of passing the information between the server and the application is the Common Gateway Interface. This standard lets programmers write applications in different languages that can plug into the server and interact with it. The interface guarantees consistency of data passed between the server and the application. An alternative to a CGI application is Microsoft&amp;rsquo;s Active Server Page (ASP).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CGI&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A CGI program is one that conforms to the above specified protocol. The advantage of using CGI over a server module is keeping the execution of the application separate from the server.&lt;/p&gt;
&lt;p&gt;If CGI is installed on the server a cgi-bin directory might be added to the public directory of the website and every page from that directory is treated as a CGI script, so the server runs the files through a relevant application instead of showing the code directly to the user. The server can also be configured to treat files with specific extensions as CGI files.&lt;/p&gt;
&lt;p&gt;If CGI is installed on the server, the specific cgi-bin directory is also added there, for example home/user/public_html/cgi-bin. CGI scripts are stored in this directory. Each file in the directory is treated as an executable program. When accessing a script from the directory, the server sends request to the application, responsible for this script, instead of sending file&amp;rsquo;s content to the browser. After the input data processing is completed, the application sends the output data to the web server which forwards the data to the HTTP client.&lt;/p&gt;
&lt;p&gt;When PHP is run in a CGI mode the server knows the location of the executable file and the loads it, with all its settings from the php.ini file every time it needs to interpret a php file. With this kind of workflow comes a lot of repeated work. The advantage is slightly better security, because the code is isolated from the server and the ability to load the php settings directly, without having to restart the server.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fast CGI&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The original CGI wasn&amp;rsquo;t very efficient and the FastCGI interface was introduced to deal with some of thess performance issues. One of the differences with the original is that the process spawned to handle the request is kept alive longer, so it can handle also some future requests.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;FPM&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;FPM which stands for FastCGI Process Manager is a FastCGI implementation which has some features geared toward heavily trafficked websites. It maintains a pool of workers that can handle PHP requests and should avoid the memory overload from PHP in Apache processes. You can check out a list of features on the &lt;a href=&#34;https://www.php.net/manual/en/install.fpm.php&#34;&gt;php.net&lt;/a&gt; website.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Apache Module&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In case of using an Apache Module every child process that Apache spawns will contain its own copy of PHP interpreter. The downside of this solution is that the footprint of every spawned process is higher, so it consumes more server resources. In order to apply new PHP settings a server restart is required, but on the other hand PHP-specific settings can be included in the .htaccess files.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Finding out PHP server interface</title>
      <link>https://lzag.dev/finding-out-php-server-interface/</link>
      <pubDate>Sun, 10 May 2020 22:14:04 +0000</pubDate>
      <guid>https://lzag.dev/finding-out-php-server-interface/</guid>
      <description>&lt;p&gt;There are many ways of executing a PHP script. For example you might run it directly from a command line:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;php &amp;lt;name_of_the_script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Other option, readily available for tesing since PHP 5.4, is to spin up the internal PHP server and see the result in a browser:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;php -S localhost:3000
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you create a simple page with the function &lt;a href=&#34;https://www.php.net/manual/en/function.php-sapi-name.php&#34;&gt;php_sapi_name&lt;/a&gt; you will be able to check out the interface that is being used to run the script.&lt;/p&gt;</description>
      <content>&lt;p&gt;There are many ways of executing a PHP script. For example you might run it directly from a command line:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;php &amp;lt;name_of_the_script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Other option, readily available for tesing since PHP 5.4, is to spin up the internal PHP server and see the result in a browser:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;php -S localhost:3000
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you create a simple page with the function &lt;a href=&#34;https://www.php.net/manual/en/function.php-sapi-name.php&#34;&gt;php_sapi_name&lt;/a&gt; you will be able to check out the interface that is being used to run the script.&lt;/p&gt;
&lt;p&gt;In the first case the out put should be &amp;ldquo;cli&amp;rdquo; in the second one &amp;ldquo;cli-server&amp;rdquo;. If you run the file on a server it could output &amp;ldquo;apache2handler&amp;rdquo; or something similar if you&amp;rsquo;re using Apache server.&lt;/p&gt;
</content>
    </item>
    
    <item>
      <title>Installing Let’s Encrypt SSL certificate</title>
      <link>https://lzag.dev/installing-lets-encrypt-ssl-certificate/</link>
      <pubDate>Sun, 10 May 2020 00:40:23 +0000</pubDate>
      <guid>https://lzag.dev/installing-lets-encrypt-ssl-certificate/</guid>
      <description>&lt;p&gt;Https has become a standard for websites and there&amp;rsquo;s no excuse not to have it enabled on the website, especially that it&amp;rsquo;s now possible for free.&lt;/p&gt;
&lt;p&gt;Making your website more secure with a free Let&amp;rsquo;s Encrypt certificate is very easy with &lt;a href=&#34;https://certbot.eff.org/&#34;&gt;Certbot&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The prerequisites are to have SSH access to your server that is already online and serving the site over http. In the example I&amp;rsquo;m setting up the certificates with Apache.&lt;/p&gt;</description>
      <content>&lt;p&gt;Https has become a standard for websites and there&amp;rsquo;s no excuse not to have it enabled on the website, especially that it&amp;rsquo;s now possible for free.&lt;/p&gt;
&lt;p&gt;Making your website more secure with a free Let&amp;rsquo;s Encrypt certificate is very easy with &lt;a href=&#34;https://certbot.eff.org/&#34;&gt;Certbot&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The prerequisites are to have SSH access to your server that is already online and serving the site over http. In the example I&amp;rsquo;m setting up the certificates with Apache.&lt;/p&gt;
&lt;p&gt;You would need to add the repository for Certbot first:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;    sudo apt-get update
    sudo apt-get install software-properties-common
    sudo add-apt-repository universe
    sudo add-apt-repository ppa:certbot/certbot
    sudo apt-get update
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After Certbot and the Apache plugin are installed you need to install the certificate with the server:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;sudo apt-get install certbot python3-certbot-apache
sudo certbot --apache
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;During the installation you will need to answer some configuration questions like which domains that you would like to include, but they are pretty self-explanatory.&lt;/p&gt;
&lt;p&gt;And that&amp;rsquo;s it! Your server is ready to serve the site over https.&lt;/p&gt;
</content>
    </item>
    
  </channel>
</rss>
