<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Bosun Egberinde</title>
        <link>https://www.bosunegberinde.com</link>
        <description>Writing on software, engineering, and weekly notes.</description>
        <lastBuildDate>Sat, 22 Aug 2026 12:57:30 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Bosun Egberinde</title>
            <url>https://www.bosunegberinde.com/favicon.ico</url>
            <link>https://www.bosunegberinde.com</link>
        </image>
        <copyright>All rights reserved 2026</copyright>
        <item>
            <title><![CDATA[When Respect Becomes Deference]]></title>
            <link>https://www.bosunegberinde.com/articles/when-respect-becomes-deference</link>
            <guid>https://www.bosunegberinde.com/articles/when-respect-becomes-deference</guid>
            <pubDate>Sat, 22 Aug 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A viral exchange at a UBA graduation raises a larger question: how do we preserve a culture of respect without making powerful people impossible to question?]]></description>
            <content:encoded><![CDATA[<p>At a recent graduation ceremony for United Bank for Africa&#x27;s Graduate Management Accelerated Programme, a young woman stood up to ask Tony Elumelu a question.</p>
<p>&quot;Good morning, Tony,&quot; she began.</p>
<p>Elumelu initially appeared to hear &quot;Toyin,&quot; so she repeated herself: &quot;I said good morning, Tony.&quot; He then told her not to call him Tony and gave her alternatives: Mr Elumelu, T.O.E., or Chairman. &quot;I don&#x27;t subscribe to that kind of <span class="relative inline" data-headlessui-state=""><button class="font-inherit inline cursor-help border-0 bg-transparent p-0 font-semibold text-inherit underline decoration-[#9ec900] decoration-2 underline-offset-4 transition-colors hover:decoration-black focus:outline-none focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-[#9ec900] dark:decoration-[#d9ff66] dark:hover:decoration-white dark:focus-visible:outline-[#d9ff66]" aria-label="Show definition for oyinbo" type="button" aria-expanded="false" data-headlessui-state="">oyinbo</button></span><span hidden="" style="position:fixed;top:1px;left:1px;width:1px;height:0;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0;display:none"></span> life,&quot; he added.</p>
<p>She corrected herself. &quot;Good morning, Mr Elumelu.&quot; Only then did she continue with her question.</p>
<p>The clip is less than thirty seconds long. I was not at the event and, at the time of writing, I have not seen the full interaction. I do not know what the participants were told beforehand or which rules of engagement had been established. That limitation matters. A short clip should make us careful about declaring anyone&#x27;s intent.</p>
<p>But the exchange still tells us something. More importantly, the arguments people have made in response to it tell us something about how we understand respect, culture, and power.</p>
<p>My straightforward view is that the woman was not clearly wrong—not from the context available to us. Elumelu was entitled to say how he preferred to be addressed. What I question is how that preference was communicated and why so many people believe she should have known it without being told.</p>
<h2>A preference is not an unwritten rule</h2>
<p>Everyone should be able to choose how they want to be addressed. This applies to names, titles, pronunciations, and pronouns. If someone tells me they prefer &quot;Mr Elumelu&quot; to &quot;Tony,&quot; there is nothing unreasonable about respecting that preference.</p>
<p>The difficulty is that people cannot know a personal preference the first time they meet us. There has to be room for a good-faith mistake and enough grace to correct it.</p>
<p>&quot;I prefer Mr Elumelu&quot; and &quot;you won&#x27;t call me Tony&quot; communicate roughly the same preference, but they do not communicate the same relationship. One states a boundary. The other also reminds the listener who has the power to enforce it.</p>
<p>That distinction becomes more important in a room where one person is a graduate trainee and the other is the chairman of the organisation. There was never any doubt about who had more status. The young woman could not respond with the same force, even if she believed the correction was unfair. Grace should matter most where the difference in power is greatest.</p>
<p>Some people have argued that this was a formal corporate event and that she should have addressed the chairman accordingly. That argument depends on the organisation&#x27;s actual rules.</p>
<p>If UBA had clearly told its trainees to use formal titles at the ceremony, then she broke a known rule. That would be a straightforward workplace matter. But others familiar with corporate banking have said that employees are often trained to address people by their first names. If that was also the culture communicated to these graduates, then the problem is not that she failed to read the room. The organisation gave her conflicting signals.</p>
<p>An institution is allowed to define its culture. What it should not do is leave a rule unstated, enforce a different rule when a powerful person enters the room, and then blame the least powerful person for not anticipating the exception.</p>
<h2>What exactly is the cultural context?</h2>
<p>The most common defence I have seen is that she failed to understand the cultural context. Perhaps calling a senior executive by his first name is acceptable in the United States or Europe, the argument goes, but not in Nigeria. She should have read the room.</p>
<p>My question is: what exactly was she expected to read?</p>
<p>Was the rule about age? His position in the company? His wealth? The formality of the ceremony? Was it a rule of UBA, a general Nigerian rule, or Elumelu&#x27;s personal preference? Each explanation creates a different obligation, yet they are often collapsed into the phrase &quot;cultural context&quot; as if its meaning is obvious to everybody.</p>
<p>Nigeria does not have one uniform cultural context. Neither does Yoruba culture, which is the tradition I know most personally. Even practices that appear familiar can change from one community or family to another.</p>
<p>Where I grew up, a girl or woman may kneel when greeting an elder, while a boy or man may prostrate. But there are different ways of kneeling that are accepted in some Yoruba communities and regarded as inadequate in others. We also do not perform these greetings for every older stranger we encounter. The rule changes with the relationship, the setting, and sometimes simply how safe or comfortable we feel.</p>
<p>These expectations are rarely handed to us as a written system. We absorb them. When a child asks why, the answer is often some version of: because you must show respect.</p>
<p>Even the language through which we express these practices changes. I grew up calling my older siblings &quot;Brother&quot; followed by their names. &quot;Brother&quot; is English, not Yoruba, but the title carries a Yoruba understanding of seniority into the English language. Calling someone &quot;Mr&quot; is also not an indigenous Nigerian invention. These are already hybrid practices: local values expressed using forms inherited from somewhere else.</p>
<p>That does not make them meaningless. I still use some of them because they communicate affection and respect to people I care about. But it should make us cautious about declaring one expression timeless, authentic, and compulsory while dismissing another as &quot;oyinbo life.&quot; Culture has never been that pure or that static.</p>
<h2>Respect for whom?</h2>
<p>There is much I value in the culture of respect I grew up with. We are taught to recognise elders, welcome strangers, honour traditional leaders, and behave with consideration toward the people around us. Those are good instincts.</p>
<p>But our practice of respect often moves in one direction: upwards.</p>
<p>The younger person must respect the older person. The employee must respect the executive. The poor person must respect the wealthy person. The politically powerless person must respect the political father. Meanwhile, the person receiving all this respect is sometimes permitted to speak to those below them without the same care.</p>
<p>This is where respect becomes deference. It stops being a mutual recognition of human dignity and becomes an instruction not to challenge someone above you.</p>
<p>I know this from experience. I have been in situations where an older person said something I knew was wrong—something I could demonstrate was wrong—but I could not say so. The cultural demand was not merely that I correct them politely. It was that I should not correct them at all.</p>
<p>The same pattern appears in families where a husband&#x27;s authority means a wife has little practical input, regardless of who understands the matter better. It appears in politics when the identity of a powerful person changes how we respond to their conduct. It appears whenever &quot;Do you know who I am?&quot; is treated as a serious argument.</p>
<p>Once a person&#x27;s status determines whether they can be corrected, the problem is no longer etiquette. It is accountability.</p>
<h2>Can we innovate without criticism?</h2>
<p>The setting of this exchange makes that question difficult to ignore. In <a href="https://www.linkedin.com/posts/africasglobalbank-gmap2026-empoweringyoungafricans-ugcPost-7496568109701582850-pbKv">its post celebrating the 374 members of this GMAP cohort</a>, UBA described their shared ambition as being &quot;to lead, innovate and make an impact across Africa and beyond.&quot; The programme has now run for 21 cohorts and trained more than 5,000 young Africans.</p>
<p>But what kind of organisational culture helps a young professional innovate?</p>
<p>Innovation requires criticism. We improve a product because someone can identify what is not working. We correct a process because someone is allowed to challenge the way it has always been done. We avoid a bad decision because the most junior person in the room can point out something the most senior person has missed.</p>
<p>None of this requires rudeness. Disagreement can be thoughtful, evidence-based, and kind. But it must be possible.</p>
<p>If employees carry a version of cultural respect into the workplace that makes superiors difficult to question, then we also carry its consequences. Can a junior employee challenge an unfair decision? Can they report mistreatment by a highly placed executive? Can they tell a leader that an idea will not work? Can they do so without first calculating whether the truth itself is disrespectful?</p>
<p>We cannot celebrate young people as the future of African innovation while training them to believe that authority is beyond correction. We cannot ask them to lead and, at the same time, teach them that leadership means knowing when to remain silent before a more powerful person.</p>
<h2>Culture is something we continue to choose</h2>
<p>None of this means that Nigeria should import every Western convention wholesale. The choice is not between preserving our culture exactly as it is and replacing it with someone else&#x27;s.</p>
<p>Culture is a way of life practised by a group of people. It persists because people continue to practise it, and it changes when their circumstances and convictions change. The Nigerian culture we have today is not identical to the culture of a hundred years ago. The culture a hundred years from now will not be identical to ours.</p>
<p>The useful question is not simply whether a practice is cultural. It is what the practice helps us preserve and what it costs us.</p>
<p>If the value underneath a title or a greeting is respect, we can preserve that value while reconsidering a ritual that produces fear, silence, or inequality. A younger person can respect an elder without surrendering the right to disagree. An employee can respect a chairman without pretending the chairman cannot be wrong. A leader can state how they want to be addressed without publicly diminishing the person who did not already know.</p>
<p>Culture should help us live well together. When part of it prevents criticism, protects power from accountability, or closes the door to better ideas, refinement is not betrayal. It is how a living culture remains worth preserving.</p>
<p>You are free to believe the woman in the video was wrong. But look beyond the instinctive answer and ask yourself why. Was it because she violated a rule she could reasonably have known? Because she failed to honour a preference nobody had communicated? Or because, somewhere along the way, we learned that a person&#x27;s age, wealth, or position should change not only how politely we speak to them, but whether we are allowed to meet them as an equal human being?</p>]]></content:encoded>
            <author>bosun@egberinde.com (Bosun Egberinde)</author>
        </item>
        <item>
            <title><![CDATA[Between Here and Home]]></title>
            <link>https://www.bosunegberinde.com/articles/bhah</link>
            <guid>https://www.bosunegberinde.com/articles/bhah</guid>
            <pubDate>Sat, 14 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A personal reflection on moving abroad from Nigeria to Germany; the loneliness, distance from family, and the quiet realities people rarely talk about.]]></description>
            <content:encoded><![CDATA[<img alt="A man in Berlin" loading="lazy" width="1280" height="853" decoding="async" data-nimg="1" style="color:transparent" srcSet="/_next/image?url=%2F_next%2Fstatic%2Fimmutable%2Fmedia%2Freality-lg.437rd-aj7sxzv.jpeg&amp;w=1920&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fimmutable%2Fmedia%2Freality-lg.437rd-aj7sxzv.jpeg&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fimmutable%2Fmedia%2Freality-lg.437rd-aj7sxzv.jpeg&amp;w=3840&amp;q=75"/>
<p>Right from when I was very young, I always imagined what it would be like to live abroad.</p>
<p>Back in Nigeria, we simply call it <span class="relative inline" data-headlessui-state=""><button class="font-inherit inline cursor-help border-0 bg-transparent p-0 font-semibold text-inherit underline decoration-[#9ec900] decoration-2 underline-offset-4 transition-colors hover:decoration-black focus:outline-none focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-[#9ec900] dark:decoration-[#d9ff66] dark:hover:decoration-white dark:focus-visible:outline-[#d9ff66]" aria-label="Show definition for abroad" type="button" aria-expanded="false" data-headlessui-state="">abroad</button></span><span hidden="" style="position:fixed;top:1px;left:1px;width:1px;height:0;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0;display:none"></span>. It&#x27;s almost a concept of its own. Everyone knows about it, everyone talks about it, and in many ways it becomes something people quietly aspire to. Growing up, you see people who traveled overseas and later returned home, and when they come back, life often appears much better for them than for those who stayed. The way their lives are presented, sometimes intentionally, sometimes not, makes it seem like moving abroad is a clear step up.</p>
<p>Because of that, the idea slowly settles into you. It becomes something you hope for. In many families, it is even prayed for. Parents pray that their children will travel abroad, and it becomes a kind of symbol of success. When you grow up in that environment, that perspective naturally shapes your expectations. You start to look forward to the same thing, and eventually, for many people, leaving the country becomes a goal.</p>
<p>But after traveling thousands of kilometers to live in another country, I now see the picture very differently. When you are back home, what you mostly see is the glamorous part - you see the results, not the process. You see the polished version of someone&#x27;s life abroad, but not necessarily the challenges that come with it.</p>
<p>This isn&#x27;t a write-up about the good parts of living abroad. There are many of those, and maybe that&#x27;s a story for another time. Instead, this is about the parts that people rarely talk about.</p>
<hr/>
<h2>The Quiet Loneliness</h2>
<p>One of the things that doesn&#x27;t get discussed enough is how lonely living abroad can be. Of course, there are communities - African communities, Black communities, and different groups who organize events and gatherings - but compared to the kind of community many of us grow up with in Nigeria, it can feel mechanical.</p>
<p>Back home, relationships are more organic. People just show up at your place in the evening. You visit someone without planning weeks ahead. Conversations happen naturally. Here, it often revolves around scheduled events or specific gatherings. If you want to see people, you attend those events. And while that helps, it&#x27;s still very different from the everyday closeness many of us are used to.</p>
<p>Loneliness is not always dramatic. Sometimes it&#x27;s simply the absence of the small things that used to fill your days.</p>
<hr/>
<h2>The Difficult Choices</h2>
<p>Another reality of living abroad is the kind of choices you sometimes have to make, and many of these are not easy. I once saw a situation where a close friend who had moved abroad lost one of their parents, and because of circumstances at the time, they were unable to travel back home for the funeral.</p>
<p>That situation stayed with me. Moving abroad can sometimes feel like telling yourself: <em>I&#x27;m leaving, and I don&#x27;t know when I&#x27;ll be back.</em> You don&#x27;t know when the next visit will be, or if the people you&#x27;re seeing today will still be there the next time you return. You talk on the phone and stay connected, but you can&#x27;t always predict when you&#x27;ll see someone in person again.</p>
<p>It&#x27;s a strange reality to live with, especially because so many of these moments are outside your control.</p>
<hr/>
<h2>Celebrations Without Family</h2>
<p>The distance also shows up during life&#x27;s joyful moments. I have friends who had babies while living abroad, and while that&#x27;s a beautiful milestone, the experience can be very different from what it would be back home.</p>
<p>In Nigeria, when someone is expecting a child, family naturally gathers around them. Parents, siblings, relatives - everyone becomes part of the process. They help out, they support you, they celebrate with you. But when you live abroad, that kind of support system isn&#x27;t automatically there. You often have to build it from scratch, and even when family visits, it&#x27;s not the same as having them close by.</p>
<hr/>
<h2>Starting Over With People</h2>
<p>Another big adjustment is friendships. When you leave your home country, it&#x27;s almost like pressing reset on many relationships. The friends you grew up with - people from school, from your neighborhood, from years of shared experiences - are no longer just a short visit away. Some remain back home, others move to different countries, and suddenly the simple things disappear.</p>
<p>You can&#x27;t just decide in the evening to visit a friend&#x27;s house and chat. Those everyday interactions that used to feel normal are gone. So you start again - meeting new people, building new relationships, and trying to create a new sense of community.</p>
<p>And all of this happens while you are also trying to manage your life - adjusting to a new country, doing well at your job, handling responsibilities, and figuring things out along the way. It&#x27;s a lot to balance.</p>
<p>Sometimes when I watch movies where the main character marries their high school sweetheart or still has their best friend from childhood by their side, it feels special to watch. Those stories are very possible when people remain in the same place for most of their lives, but when you move across continents, life tends to unfold differently.</p>
<hr/>
<h2>The Side People Don&#x27;t See</h2>
<p>These are some of the things people don&#x27;t always see. When someone returns home from abroad, the visible parts of their life often look good. It may seem like everything is working perfectly for them, but there are many emotional and personal challenges beneath the surface, and those parts rarely get talked about.</p>
<p>This article isn&#x27;t meant to discourage anyone from trying to improve their life or seeking opportunities in another country. Moving abroad can open doors and change lives in meaningful ways. But it&#x27;s also important to understand that the grass is not always greener on the other side - or at least, not immediately greener.</p>
<p>Sometimes I think about it - whether, knowing all of this now, I would still make the same decision. And I think I would. Not because it&#x27;s easy, or because everything turned out better, but because the alternative doesn&#x27;t quite feel like the life I was reaching for. So you find yourself holding two truths at once: that this life comes with real weight, and that you would still choose it anyway.</p>
<p>There are realities you only understand after you arrive. Some people learn to navigate them and thrive. Others struggle more deeply with them. Either way, these are parts of the story that deserve to be told too.</p>]]></content:encoded>
            <author>bosun@egberinde.com (Bosun Egberinde)</author>
        </item>
        <item>
            <title><![CDATA[The Zero Case]]></title>
            <link>https://www.bosunegberinde.com/articles/the-zero-case</link>
            <guid>https://www.bosunegberinde.com/articles/the-zero-case</guid>
            <pubDate>Sun, 25 May 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[A little dive into a subtle PHP bug that caused a terminal to freeze when the user input was '0'. This article explores the root cause, the investigation process, and the lessons learned from debugging this issue in a Docker-based terminal environment.]]></description>
            <content:encoded><![CDATA[<p>While working on  <a href="https://phpsandbox.io">PHPSandbox</a> — a browser-based IDE with a terminal that connects to a Docker container via WebSockets—I stumbled upon a subtle yet critical bug. It only manifested when the user input was the number <code>0</code>. Every time <code>0</code> was typed in the terminal interface, the terminal would freeze. No input would be accepted thereafter, and the only resolution was to restart the terminal session entirely.</p>
<p>It was one of those bugs that seemed trivial but had a surprisingly deep but simple root cause.</p>
<h2>The Setup</h2>
<p>The architecture behind the sandbox environment is quite straightforward:</p>
<ul>
<li>A WebSocket connection links the frontend to the backend.</li>
<li>The backend opens a terminal session inside a Docker container using Docker’s Exec API.</li>
<li>Input from the user is streamed to the backend, which writes to the Docker exec stream (a socket).</li>
<li>Output from the Docker process is read from a socket and sent back through the WebSocket to the frontend.</li>
</ul>
<p>Everything worked fine—until someone typed <code>0</code> in the terminal.</p>
<h2>The Investigation</h2>
<p>Initially, it wasn&#x27;t clear what was going wrong. I wasn’t even sure where to start looking. Thankfully, the issue was reproducible: as soon as a user typed <code>0</code>, the terminal became unresponsive. No further input was accepted. This I was able to repeat while probing different parts of the codebase.</p>
<p>Digging into the backend code, I observed the reading loop always breaks even when the socket connection is still healthy and it dawned on me that I have indeed found the culprit in the socket read loop. The loop looked something like this:</p>
<pre class="language-php"><code class="language-php code-highlight"><span class="code-line"><span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token variable">$chunk</span> <span class="token operator">=</span> <span class="token function">socket_read</span><span class="token punctuation">(</span><span class="token variable">$socket</span><span class="token punctuation">,</span> <span class="token number">2048</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
</span><span class="code-line">    <span class="token comment">// stream output to frontend</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<p>Here&#x27;s the catch: in PHP, the value <code>0</code> (whether as an integer or a string) is considered <em>falsy</em>. So when read returns <code>&quot;0&quot;</code>—a valid piece of output—the <code>while</code> condition evaluates to <code>false</code>, causing the loop to break prematurely.</p>
<h2>The Root Cause</h2>
<p>PHP’s type juggling is the real villain here. When evaluating the <code>while</code> condition, PHP casts the value to a boolean. Since <code>0</code> (as a number or string) evaluates to <code>false</code>, the loop exits, even though <code>&quot;0&quot;</code> is a legitimate piece of data that should be sent to the frontend.</p>
<p>This behavior caused the socket to close the session incorrectly whenever a <code>0</code> was encountered in the output stream.</p>
<h2>The Fix</h2>
<p>The solution was to avoid evaluating the loop condition directly with the socket read result. Instead, I used an infinite loop and manually handled falsy values inside the loop:</p>
<pre class="language-php"><code class="language-php code-highlight"><span class="code-line"><span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token constant boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
</span><span class="code-line">    <span class="token variable">$output</span> <span class="token operator">=</span> <span class="token function">socket_read</span><span class="token punctuation">(</span><span class="token variable">$socket</span><span class="token punctuation">,</span> <span class="token number">2048</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</span><span class="code-line">    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$output</span> <span class="token operator">===</span> <span class="token constant boolean">false</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
</span><span class="code-line">        <span class="token keyword">break</span><span class="token punctuation">;</span>
</span><span class="code-line">    <span class="token punctuation">}</span>
</span><span class="code-line">    <span class="token comment">// stream $output to frontend</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<p>Alternatively, you can preserve the <code>while</code> loop style and explicitly check that the value is not <code>false</code>:</p>
<pre class="language-php"><code class="language-php code-highlight"><span class="code-line"><span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token variable">$output</span> <span class="token operator">=</span> <span class="token function">socket_read</span><span class="token punctuation">(</span><span class="token variable">$socket</span><span class="token punctuation">,</span> <span class="token number">2048</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">!==</span> <span class="token constant boolean">false</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
</span><span class="code-line">    <span class="token comment">// stream $output to frontend</span>
</span><span class="code-line"><span class="token punctuation">}</span>
</span></code></pre>
<p>Both approaches ensure that valid but falsy values like <code>&quot;0&quot;</code> do not cause the loop to exit prematurely.</p>
<p>By explicitly checking for <code>false</code>, I ensured that a string <code>&quot;0&quot;</code> or number <code>0</code> wouldn’t terminate the loop incorrectly.</p>
<h2>Lessons Learned</h2>
<p>This bug was a reminder of some key lessons:</p>
<ul>
<li><strong>Beware of type coercion</strong> in dynamically typed languages like PHP. Falsy values like <code>0</code>, <code>&quot;0&quot;</code>, <code>false</code>, and <code>null</code> can have surprising effects.</li>
<li><strong>Be explicit in conditions</strong> when handling stream data or socket reads. Always differentiate between an empty or zero value and a genuine error or end-of-stream.</li>
<li><strong>Tiny bugs can live quietly for a long time</strong> until a very specific condition brings them to light. </li>
</ul>
<p>It’s a subtle thing, but it reinforces the importance of precision in backend programming especially when interfacing with low-level systems like sockets.</p>]]></content:encoded>
            <author>bosun@egberinde.com (Bosun Egberinde)</author>
        </item>
        <item>
            <title><![CDATA[Creating Standalone PHP App Binaries using Static PHP CLI]]></title>
            <link>https://www.bosunegberinde.com/articles/building-php-binary</link>
            <guid>https://www.bosunegberinde.com/articles/building-php-binary</guid>
            <pubDate>Fri, 22 Dec 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[This article explores the innovative process of transforming PHP projects into standalone executable binaries using Static PHP CLI (SPC). It provides a step-by-step guide on converting a Laravel Zero application into a self-contained binary, highlighting the benefits of independence from PHP installations and the challenges of handling compilation complexities and managing binary sizes. This approach presents a new frontier in PHP application deployment, offering enhanced portability and broader distribution possibilities.]]></description>
            <content:encoded><![CDATA[<img alt="PHP Bin Image" loading="lazy" width="1792" height="1024" decoding="async" data-nimg="1" style="color:transparent" srcSet="/_next/image?url=%2F_next%2Fstatic%2Fimmutable%2Fmedia%2Fphp-binary.3e4jq-osm1ex8.png&amp;w=1920&amp;q=75 1x, /_next/image?url=%2F_next%2Fstatic%2Fimmutable%2Fmedia%2Fphp-binary.3e4jq-osm1ex8.png&amp;w=3840&amp;q=75 2x" src="/_next/image?url=%2F_next%2Fstatic%2Fimmutable%2Fmedia%2Fphp-binary.3e4jq-osm1ex8.png&amp;w=3840&amp;q=75"/>
<p><strong>TL;DR:</strong> This article explores the innovative process of transforming PHP projects into standalone executable binaries using Static PHP CLI (SPC). It provides a step-by-step guide on converting a Laravel Zero application into a self-contained binary, highlighting the benefits of independence from PHP installations and the challenges of handling compilation complexities and managing binary sizes. This approach presents a new frontier in PHP application deployment, offering enhanced portability and broader distribution possibilities.</p>
<hr/>
<p>Some weeks back I made a post on <a href="https://twitter.com/bosunski/status/1727728620599460346">X</a> concerning <a href="https://github.com/crazywhalecc/static-php-cli">Static PHP CLI</a>, a tool that facilitates building a static binary of PHP with extensions included. The post seems interesting to some people and i thought I should expand more on my thoughts and findings on it since I have been tinkering with it.</p>
<p>Since PHP is an interpreted language, an interpreter is required to run the PHP codes wherever they are deployed to. Unlike compiled languages where code written can be compile and ran directly by the machines they are deployed to. This slight difference already introduced more complexities to how deployments are done for a language like PHP. These complexities are mostly around ensuring the target machine has requisite dependencies, configuration and the interpreter to run the code. This limitation also, sometimes, by extension, limit where PHP apps can be used. Even though, PHP apps can be packaged as a <a href="https://www.php.net/manual/en/intro.phar.php">PHAR</a> (PHp ARchive), which can combine the app code and its dependencies as one, it doesn&#x27;t solve the problem of the interpreter required to run it.</p>
<p>In other words, the farthest we’ve been able to go as far as having a PHP project as a single unit is a PHAR file, which itself depends on having PHP installed wherever it will be used. The further process of making a PHAR file into a single executable binary have some more complex parts, and this is where the Static PHP CLI (SPC) project comes to play. It automates the process of compiling PHP SAPIs (CLI, FPM and MicroSFX) as static binaries along with the extensions, while presenting you with a CLI to customise this process. It also allows you to combine resulting static binaries with your project to produce a single binary with all dependencies and configuration baked in, presenting a new process of shipping PHP apps.</p>
<p>Before I delve into most of my thoughts, let’s see how we can make use of this project in practice for a full-blown application. We will use a CLI app based on <a href="https://laravel-zero.com/">Laravel Zero</a> - a framework for building CLI apps in PHP that is based on Laravel. I choose to go with a project like this as it is common for PHP projects to have dependencies, some structure, etc. So, I think using a single PHP script will be oversimplified and might inspire the doubt that you cannot use this process in a standard PHP project.</p>
<p>A CLI application is also chosen since, most benefit of this method of shipping a PHP project comes to light this way. A high-level process of this example will look more or less like this:</p>
<img alt="Compilation Process" loading="lazy" width="2208" height="1116" decoding="async" data-nimg="1" style="color:transparent" srcSet="/_next/image?url=%2F_next%2Fstatic%2Fimmutable%2Fmedia%2Fprocess-excalidraw.0r1xslw2puept.png&amp;w=3840&amp;q=75 1x" src="/_next/image?url=%2F_next%2Fstatic%2Fimmutable%2Fmedia%2Fprocess-excalidraw.0r1xslw2puept.png&amp;w=3840&amp;q=75"/>
<h3>Creating a project using Laravel Zero</h3>
<p>First, let&#x27;s create a Laravel Zero application called &quot;quote&quot; using this  command and then enter the root:</p>
<pre><code class="code-highlight"><span class="code-line">composer create-project --prefer-dist laravel-zero/laravel-zero quote &amp;&amp; cd quote
</span></code></pre>
<p>Once the project is created you need to rename it like this, to make sure subsequent PHAR files built from it are named as expected:</p>
<pre class="language-shell"><code class="language-shell code-highlight"><span class="code-line">php application app:rename quote
</span></code></pre>
<p>Next, let&#x27;s test the application using the <code>inspire</code> command like this, which displays a beautiful quote:</p>
<pre class="language-shell"><code class="language-shell code-highlight"><span class="code-line">php quote inspire
</span></code></pre>
<p>Now that the app is working as expected, we will build it as a PHAR - consisting of the code and Composer dependencies. This is already a feature provided by Laravel Zero and you can run this to build the PHAR:</p>
<pre><code class="code-highlight"><span class="code-line">php quote app:build
</span></code></pre>
<p>This would generate a file named <code>quote</code> inside the <code>builds</code> directory. This file is a PHAR file even though it is without the extension. We will need this file later in the process, but for now, we can verify that the generated PHAR file works like this:</p>
<pre class="language-shell"><code class="language-shell code-highlight"><span class="code-line">./builds/quote inspire
</span></code></pre>
<p>Even though it <em>seems</em> like it&#x27;s a self-executing binary, it isn&#x27;t because it relies on the presence of PHP to run, which is obviously is present at this stage.</p>
<p>Now that we have the application itself prepared to be compiled as a single binary, we need to prepare SPC in the next steps and then do the final compilation of the app into a single binary.</p>
<h3>Installing Static PHP CLI (SPC)</h3>
<p>There are a couple of ways you can use to setup the SPC project. You can either clone the project and set it up or use pre-compiled self-executing SPC binary (dog-fooding much!). I prefer the later since it&#x27;s much faster and easier to use that way.</p>
<p>We can use the <a href="https://cli.github.com/">GitHub CLI</a> to easily download a release. You can also use <code>cURL</code> or any other tool/way you want. I will stick to downloading the binary at the root of the project to keep things simple. You can find the appropriate release for your OS here on the project&#x27;s release <a href="https://github.com/crazywhalecc/static-php-cli/releases">page</a>.</p>
<pre class="language-shell"><code class="language-shell code-highlight"><span class="code-line">gh release <span class="token parameter variable">--repo</span> crazywhalecc/static-php-cli download <span class="token parameter variable">-p</span> spc-<span class="token operator">&lt;</span>YOUR OS<span class="token operator">&gt;</span>-aarch64.tar.gz
</span></code></pre>
<p>Next, we can extract, and then set permissions for the binary:</p>
<pre class="language-shell"><code class="language-shell code-highlight"><span class="code-line"><span class="token function">tar</span> <span class="token parameter variable">-xzf</span> spc-macos-aarch64.tar.gz <span class="token operator">&amp;&amp;</span> <span class="token function">chmod</span> a+x spc
</span></code></pre>
<p>With this, you have an <code>spc</code> binary at the root of the project, ready to use and independent of your PHP installation. You can test the binary to see everything is fine:</p>
<pre class="language-shell"><code class="language-shell code-highlight"><span class="code-line">./spc
</span></code></pre>
<h3>Preparing SPC</h3>
<p>Now that we have SPC, we need to prepare it to get it ready for use. The preparation process covers the downloading of some packages and libraries it needs to to compile a final PHP executable with extensions. The other part of the this process is to compile the PHP binary itself together with the extensions.</p>
<p>Using the <code>spc</code> binary you setup in the previous section, we need to download all the source code required for compilation like this:</p>
<pre class="language-shell"><code class="language-shell code-highlight"><span class="code-line"><span class="token assign-left variable">EXTENSIONS</span><span class="token operator">=</span><span class="token string">&quot;bcmath,calendar,ctype,curl,dba,dom,exif,filter,fileinfo,iconv,mbstring,mbregex,openssl,pcntl,pdo,pdo_mysql,pdo_sqlite,phar,posix,readline,simplexml,sockets,sqlite3,tokenizer,xml,xmlreader,xmlwriter,zip,zlib,sodium&quot;</span>
</span><span class="code-line">
</span><span class="code-line">./spc download --with-php<span class="token operator">=</span><span class="token number">8.2</span> --for-extensions<span class="token operator">=</span><span class="token string">&quot;<span class="token variable">$EXTENSIONS</span>&quot;</span>
</span></code></pre>
<p>The first part of this script defines the extensions we want to download packages for while the other part actually downloads the packages. The extensions you select are dependent on what your application needs to run properly. For Laravel Zero, the ones I have up there should suffice. Instead of the <code>--for-extensions</code> option, we can use the <code>--all</code> flag, which downloads all packages instead.</p>
<blockquote>
<p>Sometimes, I notice that, download fails when an extension requires download to be made from a Chinese source. I mostly use VPN to bypass this on my local machine making me conclude that, its <em>probably</em> a location or ISP based issue. In the CI, this doesn&#x27;t seem to happen and download should work fine.</p>
</blockquote>
<p>Now that the packages are downloaded, we are ready to build the static binaries. In this aspect, there are three options - PHP CLI, PHP FPM and MicroSFX. (Needs more clarity) PHP distributions ships with two SAPIs, the CLI SAPI for command line applications and FPM (used with Apache, Nginx, etc.). In PHP distributions they are separate and you can instruct SPC to build any or both of these binaries depending on what you want. The last one is the <a href="https://github.com/easysoft/phpmicro">MicroSFX</a> SAPI which works behind the scene as the glue between a PHP application code and the PHP interpreter.</p>
<p>The MicroSFX SAPI is likened to be a self-extracting archive, such that when launched(or executed), it unpacks it&#x27;s own content. Instead of unpacking a content in this case, it executes a PHP file or PHAR attached (combined) with it. It&#x27;s able to do this by handing the attached file over to the PHP interpreter that is embedded inside of it. You can read more on the MicroSFX SAPI <a href="https://github.com/easysoft/phpmicro/wiki/What-is-PHPmicro#phpmicro-%E8%AE%A9%E4%BD%A0%E7%9A%84php%E7%A8%8B%E5%BA%8F%E5%83%8Fgo%E4%B8%80%E6%A0%B7%E4%B8%80%E5%A4%84%E6%89%93%E5%8C%85%E5%A4%84%E5%A4%84%E8%BF%90%E8%A1%8C">here</a></p>
<p>For our app to work, we only need to build the CLI and the MicroSFX SAPIs since our Laravel Zero app will only run in the command line. We can build the SAPIs like this:</p>
<pre class="language-shell"><code class="language-shell code-highlight"><span class="code-line">./spc build --build-micro --build-cli <span class="token string">&quot;<span class="token variable">$EXTENSIONS</span>&quot;</span>
</span></code></pre>
<p>In addition to the obvious flags specifying the SAPIs, we also passed the extensions that will be compiled together with the SAPIs. Depending on the extensions you select and the specs of the computer you are running, this process could take while. The brighter part is that you only have to do this once, as long as you don&#x27;t need extra extensions as this would require another compilation.</p>
<p>At this point you should have <code>php</code> and <code>micro.sfx</code> executables present in the <code>buildroot/bin</code> directory of the current project.</p>
<h3>Combining micro.sfx and PHAR file</h3>
<p>Now that the major part, the compilation of the SAPIs, is done, we can now compile the application itself into a single binary. SPC allows you to compile a single file script as well as a PHAR into a single binary. Since we already built the application into a PHAR under <code>builds</code> directory, the next step is for us to &quot;combine&quot; the PHAR file and the <code>micro.sfx</code> file as one. We can do this using the <code>micro:combine</code> command of the <code>spc</code> binary like this:</p>
<pre class="language-shell"><code class="language-shell code-highlight"><span class="code-line">./spc micro:combine builds/quote <span class="token parameter variable">-O</span> bin/quote
</span></code></pre>
<p>This generates a new file inside the <code>bin</code> directory. A new file that shelters both the application and MicroSFX which has the interpreter. You will notice the size of the final binary is significantly larger than the initial PHAR file under <code>builds/quote</code> as a result of this combination.</p>
<p>You can then test this binary to be sure everything works fine:</p>
<pre class="language-shell"><code class="language-shell code-highlight"><span class="code-line">./bin/quote inspire
</span></code></pre>
<h3>Verifying the binary</h3>
<p>The best way to verify this is, of course, in an environment where PHP isn&#x27;t present. You can uninstall/break (😎) your PHP installation or run the executable on another (Virtual) Machine. However you choose, you should see the binary working as expected.</p>
<hr/>
<p>With this, we are able to get a PHAR file, that depends on PHP being installed in the target environment, to a single binary that doesn&#x27;t need PHP in the target environment. Next, I try to share some of my thoughts around this exploration. These thoughts covers benefits and downsides alike.</p>
<h2>What are some benefits of this method?</h2>
<p>There are cases where you are unable to guarantee that PHP is installed where your PHP code is going to be running, so you have to make sure PHP is installed before shipping your code there. Implications of having to install PHP might differ based on needs and constraints. Some environments might not be able to use your software because they can&#x27;t (or won&#x27;t) install PHP. Also, depending on how and where you want to install PHP the issues surrounding the installation might be more. Shipping as a binary cuts out installation processes, the dependencies you need to install or configurations you need on the target where the PHP app will run.</p>
<p>Furthermore, you might not be able guarantee that PHP is not broken in the environment your software will be installed. There are cases where, after a Brew upgrade, you will discover that PHP is broken and at this point <em>EVERYTHING</em> that depends on the PHP executable is, broken as well. In a case where some tools are critical for day to day, this might be unacceptable. For example, when PHP is broken, Laravel Valet is broken and cannot rescue the situation because it also depends on PHP. Having a self-executing binary avoids all these and guarantee that the app is not at the mercy of the target&#x27;s PHP installation.</p>
<p>Another common thing is differences in PHP versions or having to be as backward compatible as much as possible knowing well that you are not sure of what version might be running on the target. A PHP app might benefit from a new feature in a new PHP version, but doesn&#x27;t mean users are bound to migrate, even for logical reasons. When you compile the app as a binary, this isn&#x27;t a problem as your users can keep whichever PHP version they want while you are able use new language features in your own software. This is particularly useful where PHP apps are distributed as CLI tools.</p>
<p>Another benefit I see, which is win for the community is that more people will be able to use tools from the PHP community since the barrier of setting PHP up is removed and the app can now function just like any binary they are used to.</p>
<p>Even more, we will be able to use PHP, combined with extensions like Swoole/Swow or Libraries like ReactPHP and Amphp to build system tools that might otherwise be built with compiled languages like Rust or Golang for the very reason of performance and portability as a single binary.</p>
<p>Another benefits of this method is that, we can now distribute PHP tools outside of Composer. Some PHP-based tools are distributed via composer to ensure the target platform have the dependencies required for the tool. Now that everything can ship as a single binary, we don&#x27;t have to distribute through Composer or limit it to Composer who also requires PHP on the target platform. Maybe in the future we can see PHP tools distributed through channels like APT and Homebrew.</p>
<h2>Some downsides</h2>
<p>This also doesn&#x27;t come with some downsides and limitations. Some of this can be desirable or not depending the people concerned.</p>
<p>One that comes to light quickly is that the process can be tedious since this isn&#x27;t something that is part of the &quot;standard&quot; way you would use to ship PHP apps. The process involves downloading dependencies and then compilation occurs. How fast the compilation of the initial static binaries will be depends on:</p>
<ul>
<li>System specs (Cores, mostly)</li>
<li>The number of extensions that will be compiled with it</li>
<li>Some extension can also take longer to compile than another</li>
<li>The SAPIs you are compiling for - since the goal is to build a binary embedding your application code, the MicroSFX and the PHP CLI is often needed</li>
<li>The amount of dependencies that will be downloaded based on the extensions</li>
</ul>
<p>Generally, the SPC package has made this as simple as possible to automate/abstract a lot of this part. It even caches the downloaded the dependencies, so it won&#x27;t be downloaded again. also, if the extensions aren&#x27;t changing much, the already compiled static PHP binaries can be used for compiling the final application binary.</p>
<p>Size is another thing when considering this method of shipping your application. Depending on the extension(s) being compiled with the static PHP binary, the size of the final binary can be huge. If size is a constraint for you, this might not be good. Compiling recommended extensions together with another project went up as high as 80Mb. The size can be reduced further by getting rid of development dependencies and only compiling what you need in the application PHAR. Since shipping without some extensions might not be good for some applications, there isn&#x27;t much choice as to what can be done to reduce the size of the generated static PHP binary.</p>
<p>Another difficulty you want to watch out for is building for other platforms. Compiling binaries in languages like Rust and Golang for other platform in the development platform is already built into their tooling. With that you can target the platform you want to build for when developing on a different platform. This isn&#x27;t possible in this case, since you need to be on the same platform as your target to compile the static PHP binary for it. A work around is to rely on a platform like GitHub Actions to build for different platforms.</p>
<p>Just incase it isn&#x27;t obvious, you won&#x27;t be able to modify the source code once the binary is built since PHP apps are not usually deployed this way. So, this is another thing to consider when choosing to go with this method as you can&#x27;t just SSH to the server and make changes 😎. I also think that, for some type of applications this shouldn&#x27;t be a problem.</p>
<p>One last thing to mention here, is that you will still need to ship your PHP code as it is if you need FPM - for applications that are served this way. The aspect of a static binary that will be of benefit here will only go as far as having a PHP FPM binary where all extensions are statically compiled together. The advantage here is that extensions won&#x27;t need to be loaded dynamically, so the app will boot up faster.</p>
<h2>What does adoption of this method currently look like?</h2>
<ul>
<li><a href="https://github.com/swoole/swoole-cli">Swoole CLI</a>, a binary distribution of PHP with Swoole extension compiled together</li>
<li><a href="http://hyperf.io/">Hyperf</a>, a Laravel-like framework based on Swoole and Swow has a tool, <a href="https://github.com/hyperf/box">Box</a>, that can compile a Swow based Hyperf application as a single binary</li>
<li><a href="https://www.workerman.net/webman">Webman</a>, a async PHP framework, based on Workerman, already <a href="https://www.workerman.net/doc/webman/others/bin.html">have</a> a version of this method of compilation as well</li>
<li><a href="https://herd.laravel.com/">Laravel Herd</a>, a new development environment for Laravel uses this method to ship multiple PHP binaries with extensions already compiled in them</li>
<li><a href="https://frankenphp.dev/docs/embed">FrankenPHP</a> , a new PHP SAPI, also use SPC to embed PHP application code and asset as a single binary</li>
</ul>
<hr/>
<p>Converting PHP projects into single binaries with SPC opens up exciting possibilities for PHP developers. While there are challenges to overcome, such as compilation intricacies and binary size considerations, the benefits, including improved compatibility and easier distribution, make this approach a compelling option. As adoption grows within the PHP community, we can anticipate further refinements and enhancements, making this method an increasingly valuable tool for PHP developers.</p>
<hr/>
<p>Do you want to share your thoughts on this article? You can leave a comment on <a href="https://twitter.com/bosunski/status/1738152399129514144">X(Twitter)</a> or <a href="https://www.linkedin.com/posts/bosunski_creating-standalone-php-app-binaries-using-activity-7143918102223757312-rHgL">LinkedIn</a>.</p>]]></content:encoded>
            <author>bosun@egberinde.com (Bosun Egberinde)</author>
        </item>
        <item>
            <title><![CDATA[Being flexible with building habits]]></title>
            <link>https://www.bosunegberinde.com/articles/being-flexible-with-building-habits</link>
            <guid>https://www.bosunegberinde.com/articles/being-flexible-with-building-habits</guid>
            <pubDate>Tue, 17 Jan 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Explore the journey of adapting to life's changes through the lens of learning German post-layoff. This personal narrative highlights the importance of flexibility in habit formation and offers insights on overcoming obstacles to maintain progress. Learn how adjusting expectations and routine can lead to sustainable habit development, even amidst life's unpredictability.]]></description>
            <content:encoded><![CDATA[<p>When I got laid off around May last year, it opened up the opportunity to explore other things or even focus more on aspects about life you didn&#x27;t had much time for. For me, it was more on the aspects of things I wanted to do, but wasn&#x27;t successful at building an habit around. So, I thought I could use that time to create a habit around them before I started working again.</p>
<p>One area in this category was learning German. I had started learning prior to the layoff personally through Babel and also attended some part-time classes in the evening after work. I specifically choose Babel after the classes ended as a way to keep me in the loop of learning and practicing but I wasn&#x27;t successful as I thought.</p>
<p>I wanted to go on Babel every single day and make sure I did something no matter how the day goes. That was the goal, but cumulatively, I couldn&#x27;t sustain this partly due to how my day-to-day work is structured and also maybe I didn&#x27;t plan well enough.</p>
<p>It did cross my mind that I could bias for less frequency and more time to compensate but I quickly dismissed the idea. I might have thought that this kind of flexibility means giving up on my own goal and I basically roughed it through that two months on Babel. It felt like I was struggling to catch up with my own goal and yet I didn&#x27;t achieved it as I wanted.</p>
<p>During the layoff, I had more time, and of course more agency of what to do with it and decided to spin the idea back again. This time, I would start the day learning German and was consistent with it months after the layoff. I got in tune so much so that I completed the A1 courses and my understanding drastically improved. It felt good and the feeling that I have finally built an habit around this, given the last failure was also great.</p>
<p>I started another job in October and then the frequency started to decline again. Initially, it started that, the time I do my German learning in the morning is overwritten by some activities at work. Then unto irregular activities across the day. Just like before, I tried to cling to the idea to “just make it work” and of course the struggle started.</p>
<p>I finally revisited the idea of adjusting the frequency to once a week and got on to work and things started to get better with less of the struggles. Given how my activities are, it&#x27;s more feasible to achieve weekly consistent learning compared to daily ones. I could see that progress was been made even though I appeared once a week and that was all I wanted after all.</p>
<p>I think often times when building habits, it&#x27;s easy to rule out being flexible even in the face of irregularities or unpredictabilities of our day-to-day or even change in priorities.</p>
<p>This is not an encouragement to stop doing what matters to me the moment it gets difficult to sustain, but to change perspectives on what can be changed around my routines to help reduce the struggles while building a habit. I know this would probably apply differently to individuals, but in a world where we sometimes don&#x27;t have control over events, being flexible to fine tune what regular means in the moment can help in guaranteeing a habit over the long term.</p>]]></content:encoded>
            <author>bosun@egberinde.com (Bosun Egberinde)</author>
        </item>
    </channel>
</rss>