<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by George Nyoro Possum on Medium]]></title>
        <description><![CDATA[Stories by George Nyoro Possum on Medium]]></description>
        <link>https://medium.com/@georgenyoropossum?source=rss-1c13bc5918c2------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*cIbhnbb2q0hsBrcG.</url>
            <title>Stories by George Nyoro Possum on Medium</title>
            <link>https://medium.com/@georgenyoropossum?source=rss-1c13bc5918c2------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 20:08:20 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@georgenyoropossum/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[“Good Enough” AI: When Technology Moves Faster Than the World]]></title>
            <link>https://medium.com/@georgenyoropossum/good-enough-ai-when-technology-moves-faster-than-the-world-05bb154bc926?source=rss-1c13bc5918c2------2</link>
            <guid isPermaLink="false">https://medium.com/p/05bb154bc926</guid>
            <category><![CDATA[anxiety]]></category>
            <category><![CDATA[llm]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[ai]]></category>
            <dc:creator><![CDATA[George Nyoro Possum]]></dc:creator>
            <pubDate>Thu, 17 Sep 2026 11:19:36 GMT</pubDate>
            <atom:updated>2026-09-17T11:23:53.674Z</atom:updated>
            <content:encoded><![CDATA[<p><em>In summary, models can supercharge our speed. But, just because you CAN doesn’t mean you SHOULD. There is a tendency to grow, anxious, but relax, take a breathe, because the fundamentals of the world haven’t changed.</em></p><p>The LLM arena is an interesting and fascinating space… fundamentally transformer and self-attention models changed the world. Language processing, contextual understanding: all took a big leap forward, and there is <strong>no going back</strong> from that.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5MpkaitkrbvX0CeCr0mv-Q.jpeg" /></figure><p>The release of the likes of <a href="https://typesafe.ai/blog/introducing-system-one-models-and-jev"><strong>Jev</strong></a> is a spanner in the works for frontier labs, and I foresee similar niche specific, distilled models being released in future. AI researchers might only grow in number, looking for different ways to create new models. Their interaction with traditional Machine Learning Models will be something of interest to watch and observe e.g. because Decision Trees still dominate bounded non-hard coded multi-parameter business rules (&gt;50), which need (say it with me) <strong>DETERMINISM</strong>.</p><p><strong>Coding</strong> is still an applicable space for Frontier LLMs, but that depends on the coding approach favoured. Cheaper long context models like DeepSeek v4 flash (not even mention newly released 4.1) are more preferable to top class Astra &amp; Fable if you are trying to keep your hand on the throttle — if you want to track logic of what is happening in your system. They are cheap enough to write boring code for you, while allowing you to go step by step, with verification. There are entire philosophies to how this works.</p><p>We are in the period of transition. People are making bets. Some are betting their entire house. It remains to be seen who wins and who loses their shirt in the end. Some have taken a more careful approach: not building systems that will turn out to be unmaintainable if unsubsidized pricing turns out to be $8000 per subscription, instead of $20 or $200.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wtfZHpDq-5nVfJ5ECaUrHQ.jpeg" /></figure><p>There is a tendency to feel like you are being left behind right now. A kind reminder: is that the world doesn’t run at the pace of infinity. You can ship code tomorrow, but are your users ready to adopt to every blinding new feature? Are the systems up/downstream ready to adopt to your new speed? You don’t need to deliver features every other day.</p><p>While the anxiety is real, what people look for is value. That has always been what we bring to the table. And the fundamentals remain, people still need to eat, sleep under a roof, and possibly, if the Lord allows, thrive in other spaces. Take a breathe, and re-evaluate.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=05bb154bc926" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Rebuild the Internet, One Project at a Time]]></title>
            <link>https://medium.com/@georgenyoropossum/rebuild-the-internet-one-project-at-a-time-f7c0a680c938?source=rss-1c13bc5918c2------2</link>
            <guid isPermaLink="false">https://medium.com/p/f7c0a680c938</guid>
            <category><![CDATA[bash]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[system-design-concepts]]></category>
            <category><![CDATA[code]]></category>
            <dc:creator><![CDATA[George Nyoro Possum]]></dc:creator>
            <pubDate>Thu, 05 Jun 2025 08:34:25 GMT</pubDate>
            <atom:updated>2025-06-05T09:24:45.310Z</atom:updated>
            <content:encoded><![CDATA[<h3>CodeCrafters: An Odd Ally Among Many</h3><blockquote>It comes highly recommended because it focusses on building more than just hacking away.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nrUV21SVMcXQJgrlFWU1FA.jpeg" /></figure><p>I have tried Hackerrank, ProjectEuler, LeetCode, Codewars and other such tools, all in a bid to improve my coding chops. Each has its own strengths and areas that it focusses on. <em>Leetcode</em>, for example, when not being used to prepare for job interviews, can be very useful in learning algorithms.</p><p>Surprisingly, grinding <em>Leetcode</em> can teach you a lot about yourself:</p><ul><li>When subjected to pressure, do you underperform or rise up to the challenge? Do you overthink and suffer from analysis paralysis?</li><li>Does your mind rush to complicated solutions at first, ignoring simpler solutions in a case of premature optimisation?</li><li>Do you tend to address edge cases before establishing that base functionality works?</li><li>When trying to debug loops, do you walk over the case of i=0, then skip to the last case? Do you skip over some code sections and assume they work? Is there a pattern in how you overlook certain aspects?</li></ul><h3><a href="https://app.codecrafters.io/">Codecrafters</a></h3><p><a href="https://app.codecrafters.io/">https://app.codecrafters.io</a></p><p>I heard about it while watching <a href="https://www.youtube.com/@t3dotgg">Theo</a> on Youtube(<em>his sponsor segments are really useful most of the time</em>). It promised the ability to rebuild, from the ground up, Redis, a shell, a HTTP Server, your own Kafka, and many more, with more to be added in future. The annual subscription appeared to be well worth the price, and even better, they offer regional pricing, with Africans getting a significant discount!</p><p>I’d recommend <a href="https://app.codecrafters.io/"><em>Codecrafters</em></a> if you like to:</p><ul><li>approach things from <strong>1st principles</strong>. Knowing how they work deep-down.</li><li>want to <strong>challenge</strong> yourself and grow your skills.</li><li>know that you have met the standard. They run your code <strong>against tests</strong> in a container to make sure it passes required specs. So it’s not you just saying that you wrote a HTTP server. They actually confirm that your server runs according to the specification guidelines they provide.</li></ul><p>Here are some of the projects that I’ve embarked on:</p><h3>1 Building a HTTP Server</h3><p>See <a href="https://github.com/geonyoro/codecrafters-http-server-python">https://github.com/geonyoro/codecrafters-http-server-python</a></p><p>This is the first module I started with and I recommend it for its simplicity. Having chosen python, this helped me understand:</p><ul><li><strong>http versions</strong>: 1.0 vs 1.1 vs 2.0 vs 3.0. Somewhere in there, you get to implement long-lived connections, which are a HTTP/2.0 thing. It now makes sense when you see HTTP/1.1 vs HTTP/2.0 and know what you can expect the server to do.</li><li><strong>The http spec</strong>: You get to know how requests are formatted, raw.</li><li>how to handle <strong>multiple connections</strong> simultaneously. (Choose threads?)</li><li>Why <strong>choosing</strong> <strong>threads</strong> for multiple connections doesn’t scale well to high loads (threads are resource intensive as opposed to green threads and other lightweight alternatives)</li><li><strong>Coding patterns </strong>and <strong>refactoring</strong>: There are a lot of things that are common to each request that may need to be handled. E.g. setting a content-length header for each response, handling encoding that has been specified by the client. How do you centralise all these?</li></ul><h3>2 Build out a shell</h3><p><a href="https://github.com/geonyoro/codecrafters-shell-python">GitHub - geonyoro/codecrafters-shell-python</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/474/1*rL83aUR9r4quyWHZbRkJyA.jpeg" /></figure><p>I’m still currently in the middle of this one and am really enjoying it. The goal is to implement a shell based on bash that supports some of the features.</p><p>It has helped me:</p><ul><li>implement the strategy pattern with built-in commands that we write (exit, echo)</li><li>write a parser (actually 2 parsers, one for the shell commands with their escapes, and one for the redirect operators to stdout and stderr). This was really fun and brain-wracking.</li><li>call external commands using the output of the parser and verify that I’m on the right track. (the cat command is the true test of whether things are okay)</li><li>testing: Instead of using the codecrafters’ testing suites that run on a git push, I wrote my own internal tests that I can use to iterate over faster. The added benefit of modularity in code is not lost on me.</li></ul><h3>A word to the Wise</h3><p><strong>Don’t cheat yourself</strong>: In these challenges, there are libraries that have already been built out for handling some of these things. In the Shell example, I was tempted to (and indeed saw many people) go around the problem of building a parser for the shell by either:</p><ul><li>using the <a href="https://docs.python.org/3/library/shlex.html">shlex</a> module to parse arguments and clean the same safely and easily</li><li>using the <a href="https://www.w3schools.com/python/ref_string_split.asp">string.split()</a> method to handle splitting the arguments</li></ul><p>The problem with the above approach is that it robs you of the joys of knowing how to parse commands, and the different approaches. I hope to learn how to write a full compiler so this part really helps me in that. In writing a raw parser that consumes characters, you need to:</p><ul><li>reach back into some of the skills built during Leetcode</li><li>analyse different approaches (implement look-ahead for characters while considering not getting index errors when you run out of characters vs implementing look-behind)</li><li>analyse how to handle different types of parsing (escape sequences vs re-directions) and perhaps create different categories of parsers for each of the 2.</li></ul><p>Because of the above, I’ve found myself moving slower than most, but getting value for money, and skills.</p><p>Once done with a section, I have learned to read the code that others have implemented and see their different approaches. There is a world to learn. This has, in turn, taught me humility. I thought I was hot stuff and implement the best code, only to read what someone else wrote and have my mind blown.</p><p>Check out my <a href="https://www.linkedin.com/in/george-nyoro-05511353/"><strong>LinkedIn</strong></a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f7c0a680c938" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Diving into the OP Set command in Redis]]></title>
            <link>https://medium.com/@georgenyoropossum/redis-set-function-with-nx-is-op-78c7667fc7f1?source=rss-1c13bc5918c2------2</link>
            <guid isPermaLink="false">https://medium.com/p/78c7667fc7f1</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[database]]></category>
            <category><![CDATA[redis]]></category>
            <category><![CDATA[software-architecture]]></category>
            <dc:creator><![CDATA[George Nyoro Possum]]></dc:creator>
            <pubDate>Thu, 06 Jun 2024 13:15:05 GMT</pubDate>
            <atom:updated>2024-06-06T13:28:55.463Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/474/1*QbdbePFAGflXjET15AUTUQ.jpeg" /></figure><blockquote>This refers to the set command, not the data structure.</blockquote><p>In my previous post, <a href="https://medium.com/@georgenyoropossum/using-redis-for-producer-consumer-queues-part-1-13c23d678f57">Using Redis for Producer/Consumer Queues: Part 1</a>, we explored using Redis lists as queues, and combined that with sets to ensure uniqueness. If the Redis list contains keys, then each key must first be confirmed to not exist in the set before pushing onto the list(queue).</p><p>A sorted set is a sort of hash-map of keys and values that are used to score the keys. Part 2 (unpublished as of the time of this publication) covers handling errors by using sorted sets (and setting the value for each key as an expiry time = current_time + expiry_timeout ). We would then iterate over the sorted set to find the values that have expired (value &lt; current_time) and then remove those from the sorted set, and re-add them to the list.</p><p>In my recent discoveries, this last part can be optimised, combining the 2 data structures of the set and the sorted set into one. Redis’s <a href="https://redis.io/docs/latest/commands/set/">set command</a> allows us to set the value of a key. It can take optional parameters (ex for seconds, px for milliseconds) to declare time validity of the the value before it is unset automatically by Redis. This means setting a key with ex=1 means that after 1 second, the value shall no longer be found.</p><p>The set command additionally can take a NX parameter, which instructs Redis to only set the key if it does not already exist. It returns a 1 or 0, depending on whether the value was set or not. In this way, we can combine these 2 data structures or not.</p><p>This method provides the advantage of being atomic (in one command, we both handle setting the value, uniqueness, and the expiry), whereas the previous use of 2 data structures was not not atomic AND may require the use of a TRANSACTION to ensure atomicity.</p><p>Previously, I wasn’t keen on using this method because I thought it would end up polluting the redis key space with many keys. If I have 1000 items to enqueue, that means I have 1000 keys in redis, where as with a set, I have 1 more data structure. However, on further examination, tools like celery (when used with redis backing) work with the key setting procedure.</p><h3>Locks</h3><p>In distributed systems, acquiring a lock isn’t a simple thing. Solutions such as Apache Zookeeper exist, but they aren’t trivial. When running a single instance of Redis(as opposed to Redis clusters), one can use the set command with NX and EX to set up a lock.</p><h3>Tips</h3><p>Use a common prefix for all keys common to a certain domain, to then allow querying with the keys command. The below uses domain.com:new_user: as the prefix.</p><pre>SET domain.com:new_user:x 1 NX EX 60 // returns 1<br>SET domain.com:new_user:y 1 NX EX 60 // returns 1<br>SET domain.com:new_user:x 1 NX EX 60 // returns 0 as domain.com:x was already set<br>KEYS domain.com:new_user:* // query all keys with the domain.com: prefix.</pre><p>Happy Redising! Or is it val-key?</p><p>Github: <a href="https://github.com/geonyoro"><strong>https://github.com/geonyoro</strong></a></p><p>LinkTree: <a href="https://linktr.ee/nyoropossum">https://linktr.ee/nyoropossum</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=78c7667fc7f1" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Python is great, until it’s not]]></title>
            <link>https://medium.com/@georgenyoropossum/python-is-great-until-its-not-c8fc525af21b?source=rss-1c13bc5918c2------2</link>
            <guid isPermaLink="false">https://medium.com/p/c8fc525af21b</guid>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[python]]></category>
            <dc:creator><![CDATA[George Nyoro Possum]]></dc:creator>
            <pubDate>Sun, 05 May 2024 21:45:23 GMT</pubDate>
            <atom:updated>2024-05-05T21:45:39.604Z</atom:updated>
            <content:encoded><![CDATA[<p>Python is quite something, until it’s not</p><blockquote>TLDR: Python falls short when it comes to runtime safety guarantees and performance</blockquote><h3>How Python Excels</h3><p>Python has come a long way from its first days in the 90s. Back when I started off in Python 2.7 in 2010 as someone new to programming, it felt extremely easy; extremely smooth. With little code, one felt productive; no public static void main(argc…..). Additionally, it supports libraries that allow a wide range of things to be accomplished:</p><ul><li>Desktop UI? Use the inbuilt tkinter library to generate GUIs</li><li>On linux for UI? Run with GTK (pretty much Ubuntu apps are written with Python and PyGTK)</li><li>Requests have never been simpler with requests</li><li>FastApi, Flask, Django for really easy servers</li><li>Data science flourishes in Python under various libraries</li></ul><p>With Python 3 and the later versions, typing hinting and the use of dataclasses were introduced to . The language itself supports various programming paradigms:</p><ul><li>Functional programming</li><li>Composition</li><li>Full OOP with inheritance</li></ul><h3>Python Falls Short: Errors</h3><p>While the adage “If it can be written in javascript, it will be&quot; exists, I have for a long time adapted that mantra for Python. Core, business components are written in Java, but other more scripted systems, and things like browser automation are written in Selenium.</p><p>This sucks because it means eventually you hit errors in production that weren’t handled. Redis ran out of memory and disconnected? Well, oops, you find that out in production when something isn’t working. Mysql can disconnect, a query can fail to execute because of a bug, Json can fail to parse correctly.</p><p>Other languages such as Java (and more recently Golang, Rust, Ziglang) force one to think critically about the errors that can come up with each function call. They each go about this differently (throwing Exceptions vs errors as values), however, the general idea is that you will know that this function can fail with x or y errors and you will have to think about handling the failure upfront. Can the program continue to work after this error or do we need to exit? In this particular error, perhaps we should send an email. This reads to very stable programs in production.</p><p>In order to catch surprise exceptions in production in Python, the closest you could get to fool-proofing is wrapping an entire loop iteration in a try/except clause, then logging that failure, and the looking at errors later. This makes for a sucky language.</p><h3>Performance</h3><p>Python is just plain slow. We can chalk up the lack of speed to it being an interpreted language, but it’s so much slower than the likes of Javascript, which is also an interpreted language.</p><p>To achieve optimisations in Python, one has to reach into sections written in C or use libraries that have optimisations in C such as numpy.</p><h3>Conclusion</h3><p>While I do appreciate Python for it’s ability to work with a vast number of libraries, and it being my first language that brought me into the world of progamming years ago, it has hindered my ability to grow past a certain threshold.</p><p>I’d like to begin to use Golang as my go-to language for almost any and all things. It offers a nice mix of speed, simple syntax, and safety.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c8fc525af21b" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[My Docker Swarm Overlay2 was full]]></title>
            <link>https://medium.com/@georgenyoropossum/my-docker-swarm-overlay2-was-full-6bcba52be047?source=rss-1c13bc5918c2------2</link>
            <guid isPermaLink="false">https://medium.com/p/6bcba52be047</guid>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[orchestration]]></category>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[docker-swarm]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[George Nyoro Possum]]></dc:creator>
            <pubDate>Fri, 01 Dec 2023 07:34:04 GMT</pubDate>
            <atom:updated>2023-12-01T07:42:22.497Z</atom:updated>
            <content:encoded><![CDATA[<blockquote>TLDR: It’s not ideal to write to filesystems on containers without mounting. Doing so creates new images saved on overlay2 that will require manual cleaning. Always write to mounts or external services.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HaWp6NvfuAJowZFfb6TGDQ.png" /><figcaption>A very fat cat: Generated from DALLE.2</figcaption></figure><p>When it comes to orchestration, almost everyone uses Kubernetes, which is quite complicated. For someone new to this world, Docker Swarm offers a simple-to-run alternative as it extends the compose file with a few more keys and arrays.</p><p>With docker swarm in production, there are 2 main errors that one encounters, and they both relate to space issues:</p><ul><li>/var/lib/docker/containers folder is full.</li><li>/var/lib/docker/overlay2 folder is full</li></ul><p>This post covers the 2nd item.</p><h3>Overlay2 is full</h3><p>While running over 100 containers in production, I noticed a degradation in service performance and a quick overview showed that one of the servers was offline.</p><p>Investigations began. I logged into the server. When servers run out of space, one of the first things to go is the auto-completion of commands on the command line under bash. I used the df -h command to check for space and confirmed that usage was at 100%. Using du -sh * starting from the root, I was able to zero in on the /var/ folder and finally the overlay2 folder in /var/lib/docker as the culprit. There was one folder there that was big. I stopped all docker services, removed the folder, started the services again, and the problem was gone. For now.</p><h3>Overlay2 Explained</h3><p>Why couldn’t I find the service related to the big folder and simply fix that? Unlike the containers folder, the naming of folders under overlay2 doesn’t correlate to the service or the container name. Therefore, it is <em>harder</em> to zero in on the cause of the problem.<em> (At least that’s what I thought at the time. A way to zero in is presented later.)</em></p><p>How does overlay2 get full? When using images in containerization, the runtime assumes that no process will write to the container’s filesystem directly. Instead, it assumes that all writes will be made to externally mounted systems or external services:</p><ul><li>For a database, we write to external Postgres/MySQL/MariaDB or a <strong>MOUNTED</strong> SQLITE file.</li><li>Logs are written to a <strong>MOUNTED</strong> directory in the file system.</li><li>Temporary files are generated to external hosts/S3 or mounted.</li></ul><p>Container images are read-only by default, in order to save space. If one wants to write to a container image, docker generates a new, writable image in the background. All writes can then proceed. This is because docker uses a <a href="https://stackoverflow.com/questions/71480296/docker-copy-on-write-cow-strategy">copy-on-write(COW) strategy</a>.</p><h3>Path to solution</h3><p>Because I love pain and suffering, I used a self-written custom solution:</p><ul><li>Install Elasticsearch</li><li>Install Metricbeat with mode: global in docker swarm(meaning a single instance per host/node on the swarm) to ship all logs to ElasticSearch</li><li>Write a monitor script in python to query ElasticSearch for space above 80%</li><li>When values exceed the threshold of 80%, send an email alert to a provided email.</li></ul><p>A few days later, I woke up to emails indicating a server was past 95%. This meant I had &lt;5% to fix this issue. I quickly narrowed down the issue to the overlay2 folder. One of the folders under this was at 245GB (<em>typical size is about &lt;2GB</em>).</p><p><strong>Isolating the faulty container</strong></p><p>While it’s hard to correlate the containers/service name with the overlay2 image name, inspecting a running container on the erroneous host will show the DIRs section that many have names related to what we see in overlay2:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZBCiahPAbSQS6v7Ck0P_yQ.png" /></figure><p>We can therefore iterate over the running containers and look for the big folder in the output using the command below.</p><pre>for i in $(docker ps -q); do <br>  echo $i; docker inspect $i | grep &lt;big_folder&gt;; <br>done</pre><p>I used exec to get into the container to see why it is so big. I found many temp files in the /tmp folder, because typically an external container is meant to be mounted and without the mount, it defaults to /tmp, as per the code below.</p><pre>import os<br>TEMP_DIR = os.environ.get(&quot;TEMP_DIR&quot;, &quot;/tmp&quot;) </pre><p>I rerun the service, mounting the necessary folders as needed, and with that, this never occurred again! :)</p><p>LEARN. GROW. PROSPER.</p><p><em>I, George “Possum” Nyoro, am a lead full-stack engineer. Checkout my linktree! </em><a href="https://linktr.ee/nyoropossum">https://linktr.ee/nyoropossum</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6bcba52be047" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Using Redis for Producer/Consumer Queues: Part 1]]></title>
            <link>https://medium.com/@georgenyoropossum/using-redis-for-producer-consumer-queues-part-1-13c23d678f57?source=rss-1c13bc5918c2------2</link>
            <guid isPermaLink="false">https://medium.com/p/13c23d678f57</guid>
            <category><![CDATA[python]]></category>
            <category><![CDATA[queue]]></category>
            <category><![CDATA[message-queue]]></category>
            <category><![CDATA[redis]]></category>
            <category><![CDATA[design-thinking]]></category>
            <dc:creator><![CDATA[George Nyoro Possum]]></dc:creator>
            <pubDate>Fri, 15 Sep 2023 07:20:13 GMT</pubDate>
            <atom:updated>2023-09-30T06:31:13.198Z</atom:updated>
            <content:encoded><![CDATA[<p><em>NB: PUB/SUB means publisher-subscriber.</em></p><p>[<a href="https://www.enjoyalgorithms.com/blog/publisher-subscriber-pattern">Source</a>]: Pub-Sub is a design pattern that allows producers and consumers to communicate with each other. A broker/middleman/middle-ware is used to send messages from a producer that are then consumed by consumers.</p><p><em>[Edit by </em><a href="https://github.com/mcflyhalf"><em>mcflyhalf</em></a><em>]</em> <em>The beauty of this is that the publisher doesn’t need to know who subscribes and the subscriber doesn’t need to know who publishes. Each party simply needs to know the topic(s) that they are interested in (sort of like Medium </em><strong><em>wink</em></strong><em>).</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ms3LB8WeVI6IWZxCM2u7AQ.png" /></figure><h3>Traditional SQL-Based Queues</h3><p>If you come from traditional relational databases like MySQL, PostgreSQL, SQLite, you are used to using a table as a queue. You put items in the table according to a schema and can delete the items in the table each time you are done or leave them there, having a field indicating that they are processed.</p><pre>create table myqueue (<br>  id integer, <br>  item_id varchar(50),<br>  value integer,<br>  dt_added timestamp null,<br>  dt_updated timestamp default current_timestamp on update current_timestamp,<br>  priority int default 0<br>)</pre><p>With the above schema, we can select the following:</p><pre><br>- id: 1<br>  item_id: &quot;item1&quot;<br>  value: 100<br>  dt_added: 2023-01-01T00:00:00<br>  dt_updated: 2023-01-02T10:02:04<br>  priority: 1<br><br>- id: 2<br>  item_id: &quot;item20&quot;<br>  value: 120<br>  dt_added: 2023-01-03T00:00:00<br>  dt_updated: 2023-01-04T10:02:04<br>  priority: 2</pre><p>This allows us to select items from the table, using it as queue with the following ordering:</p><ul><li>use the date_updated</li><li>use the priority field (either in ascending or descending order, each has it’s caveat)</li></ul><pre>SELECT * FROM myqueue ORDER BY priority DESC, dt_updated ASC; </pre><h3>Limitations of the above</h3><ul><li>The SQL-based queue is okay if you are running one process that has both consumer/producer within it. In fact, it might even scale by using threading if IO blocking was the biggest bottleneck. This doesn’t work for multiple processes.</li><li>If the process of selecting from the database is expensive by time or some other metric, e.g. selection takes 2 minutes, but you can only work on 2 items at a time.</li><li>You want to reduce the load on the SQL server by not using it as a queue. Additional support for things such as no item can be processed by more than 1 worker at the same time, recovery from failure during processing requires that YET another database table would be added, or more fields would be added to the database table, which begins to complicate its design.</li></ul><h3>V1: Basic queue in Redis</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Jed-UVwaIqf16oq5f8ATDQ.png" /></figure><p><a href="https://redis.io/">Redis</a> is an in-memory database that takes the form of a key-value store. Multiple types of values are supported e.g. bytes, lists, sets, sorted sets, and hash maps.</p><p>One of its most appealing qualities is that it doesn’t require data structure initialization meaning you don’t have to create the data structure before you use it. You can work on something that doesn’t exist, and Redis will just create it on the fly. Redis itself is also very easy to install.</p><p>For a basic queue, we use a <a href="https://redis.com/ebook/part-1-getting-started/chapter-1-getting-to-know-redis/1-2-what-redis-data-structures-look-like/1-2-2-lists-in-redis/">Redis list</a>. Values are pushed in on the right(using the RPUSH operation) and retrieved from the left(using an LPOP operation). <em>Once you LPOP an item from the list, it is no longer there.</em></p><p>Here’s some REPL code in Python.</p><pre>&gt;&gt;&gt; import redis<br>&gt;&gt;&gt; rcon = redis.Redis()<br>&gt;&gt;&gt; rcon.rpush(&quot;mylist&quot;, 1)<br>&gt;&gt;&gt; rcon.lpop(&quot;mylist&quot;)<br>1</pre><p>If you need to put more complicated data structures in there, you can JSON encode the values (or pickle.dumps() in Python ) and save the resulting bytes in Redis. (NB. It’s possible to pass strings in python, but they will be encoded into bytes before storage.)</p><pre>&gt;&gt;&gt; import json<br>&gt;&gt;&gt; rcon.rpush(&quot;mylist&quot;, json.dumps({&quot;item_id&quot;: 30, &quot;value&quot;: 230}))</pre><h3>V2: Uniqueness: Don’t re-add values</h3><p>The problem with the basic queue presented above is that it doesn’t support UNIQUENESS. One can use an external data structure where the membership of elements can be tested.</p><p>To guarantee uniqueness in Redis, we use a <a href="https://redis.com/ebook/part-1-getting-started/chapter-1-getting-to-know-redis/1-2-what-redis-data-structures-look-like/1-2-3-sets-in-redis/">SET</a>. It doesn’t allow duplication of values.</p><p>Use:</p><pre>SADD myset mykey</pre><p>This command will return a 1 if the item was added to the set, otherwise returning a 0. If the item did not exist previously in the set, you can then add it to the list.</p><p>In Python, it becomes wise to wrap these 2 steps in a function:</p><pre>from typing import Optional<br>def add_to_queue(redis_conn, val: int) -&gt; bool:<br>    added = redis_conn.sadd(&quot;myset&quot;, val)<br>    if added:<br>        redis_conn.rpush(&quot;mylist&quot;, val)<br>    return bool(added)<br><br>def pop_from_queue(redis_conn) -&gt; Optional[int]:<br>    val = redis_conn.lpop(&quot;mylist&quot;)<br>    if not val:<br>        return None<br>    redis_conn.srem(&quot;myset&quot;, val)<br>    return int(val)<br><br>&quot;&quot;&quot;<br>&gt;&gt;&gt; add_to_queue(rcon, 1)<br>True<br>&gt;&gt;&gt; add_to_queue(rcon, 1)<br>False<br>&gt;&gt;&gt; pop_from_queue(rcon)<br>1<br>&gt;&gt;&gt; pop_from_queue(rcon)<br>-1<br>&quot;&quot;&quot;</pre><p>Because Redis only works with bytes, we can’t put whole objects or dictionaries/lists into sets easily.</p><h3>V2.5 Complex Data Structures</h3><p>While the above code works well for primitives, more complex types, such as objects/maps/dictionaries can’t be added to queues because queues only support strings/bytes/integers. We can therefore serialize them into strings/bytes before adding them.</p><pre>import pickle<br>import json</pre><pre>def serialize_obj(obj):<br>  return pickle.dumps(obj)</pre><pre>def serialize_dict(mydict):<br>   return json.dumps(mydict)</pre><p>A <strong>common problem</strong> one may encounter is that not ALL attributes/keys of these data structures are meant to be used for uniqueness. For example, a financial transaction may have an identifier which is meant to guarantee uniqueness/<a href="https://en.wikipedia.org/wiki/Idempotence">idempotence</a>, but may also have a timestamp of when it happened. Sending the serialized version of this twice will result in it being added twice as well. The solution is to have a specific function for handling the computing of the key to be used in the set.</p><pre>import json<br>from typing import Optional</pre><pre>import redis<br>RCON = redis.Redis()<br>def get_key(mydict):<br>    return mydict[&quot;uuid&quot;]<br><br>def add_to_queue(redis_conn, mydict: dict) -&gt; bool:<br>    added = redis_conn.sadd(&quot;myset&quot;, get_key(mydict))<br>    if added:<br>        redis_conn.rpush(&quot;mylist&quot;, json.dumps(mydict))<br>    return added<br><br>def pop_from_queue(redis_conn) -&gt; Optional[dict]:<br>    raw_val = redis_conn.lpop(&quot;mylist&quot;)<br>    if not raw_val:<br>        return None<br>    mydict = json.loads(raw_val)<br>    redis_conn.srem(&quot;myset&quot;, get_key(mydict))<br>    return mydict<br>&quot;&quot;&quot;<br>example_dict = {<br>    &quot;uuid&quot;: &quot;1&quot;,<br>    &quot;timestamp&quot;: &quot;12312313231&quot;,<br>}<br>&gt;&gt;&gt; add_to_queue(rcon, example_dict)<br>True<br>&gt;&gt;&gt; pop_from_queue(rcon) == example_dict<br>True<br>&quot;&quot;</pre><h3>Part 2</h3><p>In the next part, coming within the next 2 weeks, we will handle more complex data structures, as well as error processing.</p><h3>From valued readers who help me with proof-reading :</h3><blockquote>What happens if the Redis service restarts or the entire server restarts?</blockquote><p>Redis can be configured to dump the in-memory values to disk based on a timeout or if a certain number of keys have been added. When starting, it will load from this particular dump.</p><p><em>[</em><strong><em>CAVEAT</em></strong><em> by </em><a href="https://github.com/mcflyhalf"><strong><em>mcflyhalf</em></strong></a><em>] </em>It is true that Redis does do a db dump, but if it does it every 5 seconds, it is possible for a crash to happen and for you to lose 4.99 seconds of data.</p><p>LEARN. GROW. PROSPER.</p><p><em>George “Possum” Nyoro is a lead full-stack engineer in a remote team and a local team. He also moonlights as a pianist/keyboardist in Nairobi. Catch him at Geco Restaurant on Fridays from 7pm.</em></p><p>Github: <a href="https://github.com/geonyoro"><strong>https://github.com/geonyoro</strong></a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=13c23d678f57" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Domain Modelling 2 : Use of Terminology and Processes]]></title>
            <link>https://python.plainenglish.io/domain-modelling-2-use-of-terminology-and-processes-75dd4dc2f55c?source=rss-1c13bc5918c2------2</link>
            <guid isPermaLink="false">https://medium.com/p/75dd4dc2f55c</guid>
            <category><![CDATA[python]]></category>
            <category><![CDATA[design-process]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[java]]></category>
            <category><![CDATA[domain-driven-design]]></category>
            <dc:creator><![CDATA[George Nyoro Possum]]></dc:creator>
            <pubDate>Fri, 05 May 2023 07:27:10 GMT</pubDate>
            <atom:updated>2023-05-11T06:42:15.849Z</atom:updated>
            <content:encoded><![CDATA[<h3>Domain Modelling 2: Use of Terminology and Processes</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/307/1*14hulhCjlKKZmHaDDkrnRw.jpeg" /></figure><h3>What are you even saying?</h3><p>In writing <a href="https://medium.com/@georgenyoropossum/domain-modelling-1-a-problem-you-didnt-know-you-have-56e072bd3c1">the previous article</a>, at some point, when talking about a function that ships orders and takes in a list/array of orders, I had initially used the word “items” instead of orders, which I promptly corrected. Yes, this entire article is based on this correction.</p><p>You may say, that’s a small thing, it can be overlooked. Just the use of the word order? But I promise you it is not. It can be a small beginning to a very painful story.</p><p>In domain modelling, a core tenet Eric Evans establishes is the use of correct domain language. This is where we establish <strong><em>ubiquitous language</em></strong> to refer to different things and concepts.</p><p>Why? Well, to take a step back, software is normally written to address real-world problems (if it is not, then more power to you). Normally, that means interaction with people outside of the programming group. One or more developers are hired by a person(s) to write software. This person is a domain expert in their subject domain (very knowledgeable on the thing they want you to solve).</p><p>There are multiple domains and people communicate across domains all the time:</p><ol><li>A business owner communicates requirements to the project manager ( OR team lead OR lead programmer). They will seldom use software language to describe the problem.</li><li>The project manager communicates with the other programmers but may use different terms that are more relevant to the developers. They can REPLACE some of the terms they used when talking to the business owner.</li><li>The business owner talks to a supplier about this new platform they are developing. Perhaps there are supplier requirements that are to be fulfilled in this software development.</li><li>The business owner talks to customers.</li></ol><p>When people talk across domains (business domain, software domain, customers domains), the terminology used is different depending on who is talking. The term order may mean an order made by a customer, but may also mean an order made by the business from a supplier. This small-term mix-up can lead to large future problems. It makes future maintenance of source code very hard. When you see the word <strong>order</strong> in the code, which of the 2 types does it refer to? It’s a problem if in each context you have to clarify which is being referred to.</p><p>When anyone involved in the process, from the business owner to the programmer, uses a certain term, it’s important that we are all on board with what it means without having to clarify a single thing.</p><h3>Business Level Understanding and Auditability</h3><p>I shared <a href="https://python.plainenglish.io/domain-modelling-1-a-problem-you-didnt-know-you-have-56e072bd3c1">my previous post</a> in excitement with friends all over but some complained that they could not understand it.</p><p>Now, I‘m not sure if this is a principle, or if I have simply intuited it, but when writing business logic, it should be written in a way as is fairly readable even by those not as technically adept. <em>If it is not readable, then it might be a sign of bad design</em>. And the design is what domain modelling is here to solve.</p><p>Take the example of the ship order function from <a href="https://medium.com/@georgenyoropossum/domain-modelling-1-a-problem-you-didnt-know-you-have-56e072bd3c1">the previous article</a>, and the requirement that some countries need phone numbers. Independent of language, a layperson should be able to understand once you tell them, for example, in Java, that the term <strong><em>null</em></strong> means empty. The logic here is easily readable.</p><p>In Java:</p><pre>if (order.getCustomer().getCountry().needsPhone) {<br>    if (order.getCustomer().getPhone() == null){<br>        throw Exception();<br>    }<br>}</pre><p>In Python:</p><pre>if order.customer.country.needs_phone:<br>  if not order.customer.phone:<br>      msg = f&quot;Customer phone is needed for country {order.customer.country}&quot;<br>      raise ValueError(msg)</pre><p>We get the country from the order’s customer and see if it requires a phone. and then we access the phone from the customer and see if it is empty.<br>Without being a fully-fledged developer, someone should be able to look at the code and generally get the flow of ideas. This introduces the idea of audibility. Here, the code being written isn’t code, but more like business logic with a light touch of language dependency.<br>The added advantage of this is that maintaining further down the road is simplified. In 5 years, you don’t have to remember what was done in this method. YOU CAN SIMPLY READ IT, and it shall make sense!</p><h3>Processes</h3><p>In my work as a back-end developer on an API at one of the local companies, a front-end developer sometimes asks me to add a certain field to a model. For example, we need to identify the supplier who will fulfill the supply for an order.</p><p>In the database, how can this be added? Well, some would add a field to hold a string, and put in the supplier name.</p><p>In the database, how can this be added? Well, some would add a field to hold a string, and put in the supplier name.</p><pre>class Order:<br>  supplier: str</pre><p>Others would add a field pointing to another table.</p><pre>class Supplier:<br>  name: str<br><br>class Order:<br>  supplier: Supplier</pre><p>The above doesn’t quite matter. What is important is PROCESS!</p><p>When the front-end developer asks to add this feature, how did they know about it? Who did they talk to? What questions did they ask? How well do they understand the business process?</p><p>These questions are important because most people aren’t modellers. It may turn out that the relationship wasn’t as straightforward. It may turn out that between an order and a supplier, there exists a CONSIGNMENT that sits between the 2.</p><pre>class Supplier:<br>  name: str<br><br>class Consignment:<br>  id: int<br><br>class Order:<br>  consignment: Consignment</pre><h3>Conclusion</h3><p><strong>If you are not in the habit of asking deeper questions on EXACTLY how the business process is achieved, you WILL be surprised later down the road. </strong>Clients love to summarize; to skip steps. They don’t think like a developer who cares about the nitty gritty. It may turn out that the relation wasn’t as straight forward. It may turn out that between an order and a supplier, there exists a CONSIGNMENT that sits between the two, thus the place of process.</p><p>Yes, this entire article is based on a correction; a very necessary one.</p><p><em>More content at </em><a href="https://plainenglish.io/"><strong><em>PlainEnglish.io</em></strong></a><em>.</em></p><p><em>Sign up for our </em><a href="http://newsletter.plainenglish.io/"><strong><em>free weekly newsletter</em></strong></a><em>. Follow us on </em><a href="https://twitter.com/inPlainEngHQ"><strong><em>Twitter</em></strong></a>, <a href="https://www.linkedin.com/company/inplainenglish/"><strong><em>LinkedIn</em></strong></a><em>, </em><a href="https://www.youtube.com/channel/UCtipWUghju290NWcn8jhyAw"><strong><em>YouTube</em></strong></a><em>, and </em><a href="https://discord.gg/GtDtUAvyhW"><strong><em>Discord</em></strong></a><strong><em>.</em></strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=75dd4dc2f55c" width="1" height="1" alt=""><hr><p><a href="https://python.plainenglish.io/domain-modelling-2-use-of-terminology-and-processes-75dd4dc2f55c">Domain Modelling 2 : Use of Terminology and Processes</a> was originally published in <a href="https://python.plainenglish.io/">Python in Plain English</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Timezone Support 2: A How To]]></title>
            <link>https://medium.com/@georgenyoropossum/timezone-support-a-how-to-36ce326df2c1?source=rss-1c13bc5918c2------2</link>
            <guid isPermaLink="false">https://medium.com/p/36ce326df2c1</guid>
            <category><![CDATA[software]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[backend]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[timezone]]></category>
            <dc:creator><![CDATA[George Nyoro Possum]]></dc:creator>
            <pubDate>Fri, 21 Oct 2022 13:02:56 GMT</pubDate>
            <atom:updated>2022-10-21T13:10:45.502Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/474/1*xuffNZQ70XUwzzmu_C4MNA.jpeg" /></figure><p>In <a href="https://python.plainenglish.io/timezone-support-normally-overlooked-2841a2ba8240">my previous post</a> on this matter, we introduced and discussed the idea of timezone support especially in applications that are meant to be used in/from different parts of the world. If all your users are in one timezone, then everyone means the same time when they say 8pm. In Kenya, 8pm means 5pm GMT+3.</p><p>We also said, in JavaScript, for example, you can automatically append the timezone to each datetime string by getting the offset in minutes, converting that to hours and minutes, and appending that to the datetime string. For Kenya, the offset will come back as -180 minutes, which is 3 hours 0 minutes.</p><blockquote>2021–01–01T0800(timezone naive) becomes 2021–01–01T0800+0300 (timezone aware).</blockquote><h3>Case Study</h3><p>A user in Kenya(GMT+3) normally schedules a clinical appointment for his mother every Tuesday at (12 pm) noon local time. One Friday, he travels to London (GMT+1) for a week.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/474/1*YUjb9mOM6x5kH-uyUHibjQ.jpeg" /></figure><p>When he goes to schedule the weekly Tuesday appointment in the app for his mother, what is he meant to put in while in London? 12pm? However, if the app auto fills in the timezone as suggested previously in JavaScript, that puts us in a conundrum. He normally means 12pm+0300 but now he will put in 12pm+0100, which means 2pm locally. His mother will show up 2 hours early for her weekly appointment.</p><p>In this case, only a savvy user might end up putting in 10am+0100 GMT to represent the exact same time, if the app were auto-filling in the timezone information.</p><p><strong><em>Is the end user supposed to keep track of time zones like this when he crosses regions? No, this complicates things for a simple user who wanted to make a 12pm booking in Kenya local time.</em></strong></p><h3><strong>Solution</strong></h3><p>My friend(and colleagu)e and I were discussing this problem and thought to check what the likes of Google does with its calendar. There is a brilliant field that I’ve overlooked despite using the calendar for years.</p><p>Instead of doing a pure automatic manual timezone calculation, a hybrid is used. Just make it obvious to the end user what timezone is being used and/or allow a selector for time zones.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1004/1*m-plz3maTyP28_SGRAKblA.png" /><figcaption>Google Calendar Add Event dialog. Click the timezone button above to make the timezone dialog appear.</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/675/1*ThWl6PMddAMdz0TcZotrWQ.png" /><figcaption>Choose a timezone.</figcaption></figure><p>Providing a drop down/selector to users makes it a bit obvious which time zones your app applies across. With this solution, the user in the case study can simply switch his timezone from GMT+1 to GMT+3 and then make his booking as usual.</p><p>Perhaps even a warning/alert could pop up to indicate that the user is in a different timezone from the event planner that he is using. A user in London could be alerted that he is currently in a different timezone from the one the app uses.</p><p>LEARN. GROW. PROSPER.</p><p><em>George Nyoro is a lead full stack engineer in a remote team, and also moonlights as a pianist/keyboardist in Nairobi. Nyoro started dabbling in python in 2010,and went full stack in 2016, and got into docker in 2020 with swarm. He is a graduate of Mechatronic Engineering at JKUAT.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=36ce326df2c1" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Timezone Support: Normally overlooked]]></title>
            <link>https://python.plainenglish.io/timezone-support-normally-overlooked-2841a2ba8240?source=rss-1c13bc5918c2------2</link>
            <guid isPermaLink="false">https://medium.com/p/2841a2ba8240</guid>
            <category><![CDATA[software]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[backend]]></category>
            <category><![CDATA[timezone]]></category>
            <dc:creator><![CDATA[George Nyoro Possum]]></dc:creator>
            <pubDate>Thu, 28 Apr 2022 11:34:54 GMT</pubDate>
            <atom:updated>2022-10-21T08:19:03.653Z</atom:updated>
            <content:encoded><![CDATA[<p>As a Python-first developer, my go-to backend setup for web based apps is Django and django-rest-framework for API support.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/500/1*2SYGs7UfQ7UqfdrJ7zqFAQ.gif" /></figure><p>I have a scheduling app that I’ve been working on and off for a few years(gaining and losing momentum, sigh, such is life). <strong>DATE and TIME is at the core of this business idea.</strong></p><p><em>PS. I highly recommend using projects to grow. It is based on my private development of this project that I have expanded my growth immensely. Every new thing I have wanted to learn I have learned in this private project.</em></p><p><a href="https://docs.djangoproject.com/en/4.0/topics/i18n/timezones/#">In a django project</a>, you can choose whether to use time zones or not, and specify the default timezone that the app will use. In a bid to conform to industry standards, I turned on timezone support and specified UTC as my timezone. <em>(Red Flag: Warning, Danger ahead.)</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/910/1*8GIox-IbK1RegbIUcZw1Yw.png" /><figcaption>From Django Website</figcaption></figure><p>When a user wanted to create an input in the schedule, they would send the start time and end time of the event to the back end. However, most of the datetimes sent would be timezone naive strings.</p><p>A <strong>timezone naive </strong>datetime is one that doesn’t specify what timezone it is in. The inverse datetime would be said to be<strong> timezone</strong> <strong>aware</strong>. You say it is Tuesday 3pm, but while it is 3pm in East Africa(EAT), it is 12 Noon in Britain(BST) and 5am in California, US(PST). <strong>If you hope to take your web based app internationally, timezone support IS A MUST</strong>!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/901/1*TcnPLwCEUvqijv78GSKWZg.png" /><figcaption>Again, from django website</figcaption></figure><p>All my targets in this alpha are in Kenya(+3 GMT). Having set up my app as UTC, when I would send in a string saying Tuesday 3pm, without timezone support, django would save the item in the backend as 3pm UTC, which is 6pm EAT.</p><p><strong>Problems</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*F2sak6t5DhRvIPVlSvxZ1w.jpeg" /><figcaption><strong><em>I don’t want peace, I want problems, only</em></strong></figcaption></figure><ul><li>When a user thus creates an event and then goes to view it, it would appear to have been shifted 3 hours ahead.</li><li>When doing any other checks either pre- or post- creation of the event which require computations with timing, they would fail, but not consistently.</li><li>When testing on localhost, you might likely not be aware of any problems, but the moment a user from another timezone wants to schedule something, it starts failing.</li></ul><p>Booking.com, airline scheduling, AirBnB and the like must have discovered these issues long ago. Sigh, I’m only discovering this right now.</p><p>Some apps will face these issues to varying degrees. Timing of checking credit card payment/mobile payment expiry may also start to matter. If you place an fast-food order, the exact timing of the placement of the order may or may not not matter. However, the timing of when it is going to be delivered might be an issue. Imagine placing an order knowing delivery takes 30 minutes and immediately being told it was delivered 5 hours ago or will be delivered 8 hours from now, only for then to hear the doorbell ring in 20 minutes.</p><p><strong>Solution</strong></p><p>Send/save timezone-aware strings.</p><p>When sending a datetime string, 2021–01–01T06:00 means 6am in UTC, 9am in +3UTC and 1am in -5UTC. If a user is in London, then that’s no problem, but users in other parts of the world should be aware.</p><p>That means, instead of</p><blockquote>2021–01–01T06:00</blockquote><p>Append the timezone information and send it in in ISO8601 format as</p><blockquote>2021–01–01T06:00+0000 for UTC</blockquote><p>or</p><blockquote>2021–01–01T06:00+0300 for +3 UTC</blockquote><p>This will help clarify.</p><p>To obtain this information for front-end use, JavaScript has a function to get the timezone offset in minutes for the user’s current region.</p><blockquote>new Date().getTimezoneOffset()</blockquote><p>This value is normally the negative value of what it’s supposed to be. .e.g +3 UTC will be given as -180 minutes, therefore one needs to subtract the value from 0.</p><p>LEARN. GROW. PROSPER.</p><p><em>George Nyoro is a lead software developer/devops in a small remote team, and also moonlights as a pianist/keyboardist in Nairobi. Nyoro started dabbling in python in 2010,and went full stack in 2016, and got into docker in 2020 with swarm. He is a graduate of Mechatronic Engineering at JKUAT(2015).</em></p><p><em>More content at </em><a href="https://plainenglish.io/"><strong><em>PlainEnglish.io</em></strong></a><em>. Sign up for our </em><a href="http://newsletter.plainenglish.io/"><strong><em>free weekly newsletter</em></strong></a><em>. Follow us on </em><a href="https://twitter.com/inPlainEngHQ"><strong><em>Twitter</em></strong></a>, <a href="https://www.linkedin.com/company/inplainenglish/"><strong><em>LinkedIn</em></strong></a>,<em> and</em><strong><em> </em></strong><a href="https://discord.gg/GtDtUAvyhW"><strong><em>Discord</em></strong></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2841a2ba8240" width="1" height="1" alt=""><hr><p><a href="https://python.plainenglish.io/timezone-support-normally-overlooked-2841a2ba8240">Timezone Support: Normally overlooked</a> was originally published in <a href="https://python.plainenglish.io/">Python in Plain English</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Domain Modelling 1: A problem you didn’t know you have]]></title>
            <link>https://python.plainenglish.io/domain-modelling-1-a-problem-you-didnt-know-you-have-56e072bd3c1?source=rss-1c13bc5918c2------2</link>
            <guid isPermaLink="false">https://medium.com/p/56e072bd3c1</guid>
            <category><![CDATA[python]]></category>
            <category><![CDATA[scalability]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[maintenance]]></category>
            <category><![CDATA[domain-driven-design]]></category>
            <dc:creator><![CDATA[George Nyoro Possum]]></dc:creator>
            <pubDate>Fri, 11 Mar 2022 09:32:28 GMT</pubDate>
            <atom:updated>2022-08-03T13:21:38.520Z</atom:updated>
            <content:encoded><![CDATA[<h3>Domain Modelling : A problem you didn’t know you have</h3><h3>Introduction</h3><p>Many developers are familiar with the term <a href="https://www.freecodecamp.org/news/big-o-notation-why-it-matters-and-why-it-doesnt-1674cfa8a23c/">Big-O notation</a>, which indicates how code scales with regard to size of input. If a function takes a list or array, it may run fast with 1 item in the array, but what about 20 million items in the array?</p><p>Is there something similar that tells us how code scales with regards to requirements? Is this even a problem? <strong>Domain Modelling</strong> is a term that refers to a frame of thinking that helps us with this. I first encountered this formal term in the book, <a href="https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215/ref=sr_1_1?keywords=domain+driven+design&amp;qid=1646990140&amp;sprefix=domain+driven+d%2Caps%2C1148&amp;sr=8-1"><strong>Domain Driven Design</strong> by <strong><em>Eric Evans</em></strong></a>.</p><h3>Example</h3><p>Let’s have a look at a sample situation. This will help us see how to arrive at a crisis later.</p><blockquote><strong><em>Week 1</em></strong>: Our company ships orders world-wide. Each order is associated with a customer, and each customer has various fields. We can model the below as follows(in <a href="https://www.python.org/"><strong>Python</strong></a>, though language barely matters):</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/436/1*VPDws-NhkjDvtWnH1ec9GA.png" /></figure><p>With the above, you start shipping orders far and wide, worldwide. <em>(It is assumed the ship_order function contains logic to ship the order as needed.)</em></p><p><strong><em>Week 2</em></strong>: Later, we are informed, that, well, we need to support a new shipping_service of shipping orders (examples of shipping_services in the real world are FedEx, DHL, US Post etc). We have been using shipping_service <strong>SSA</strong> to ship our orders, but now we need to support shipping_service <strong>SSB</strong>. The ship_order function will now use attribute <strong>shipping_service</strong> on Order.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/424/1*bbVpMC4iKndICRo2DHIt0g.png" /></figure><p><strong><em>Week 3:</em></strong> After some time, it turns out that we need to support shipping service <strong>SSC</strong>, but in addition to that, when shipping to <strong>Germany</strong> using <strong>any service</strong>, we need to make sure that the <strong>phone number</strong> is filled in before shipping the order out. Below is a simple way of solving this by altering the <em>ship_order</em> function.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/858/1*3lIoyu5ZQIh2C4cl0yScTA.png" /></figure><p><strong>Week 4</strong>: We are later informed that, due to some changes<em>(always expect changes, the real world is never constant)</em>, if we are shipping to <strong>Kenya</strong> using service <strong>SSB</strong>, we need to make sure that the <strong>postal_code</strong> is filled in. Again, we can extend the <em>ship_order</em> function.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*n6HSrE0LTiAjTvv972Qr_Q.png" /></figure><p><strong>Week 5</strong>: We are introduced to shipping_service <strong>SSD</strong>, and if an order is to be shipped using <strong>SSD</strong>, it should be <strong>moved to the top of the list</strong> of orders to be shipped. We will introduce a function sort_orders that is used to sort them orders before shipping them.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/674/1*zjy7qgFzL5jwa1FpC4gQBA.png" /></figure><p><strong>Week 6</strong>: Service <strong>SSC</strong> should include a <strong>receipt</strong> in the box of the order.</p><p><strong>Week 7</strong>: Service <strong>SSE</strong> introduced that is also a <strong>priority</strong> along with <strong>SSD</strong>, and also requires a <strong>receipt</strong>.</p><p>So far, this is what we shall have:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BtvEh2QoTbipcORAjy6dvQ.png" /></figure><p><strong>Week 50</strong>: With weekly changing requirements, after 50 weeks, the ship_order function will have grown to have become a mammoth function that is unmaintainable, supporting <strong>about 50 different</strong> if statements for the various shipping services and country situations and fields.</p><p>When a new shipping_service is added and it should be a priority higher than SSE, and should include a receipt in the box:</p><ol><li>How easy is it to extend any of the parts?</li><li>How many places in the code will we have to change things when a change is required? It would require changing the sort_single_order function to include the new function as a priority, and we should change the ship_order function in regards to the lines that handle priority. These are 2 places so far, but in the case of 50 weeks, maybe its about 10 places we need to change.</li></ol><p><strong>Clearly</strong>:</p><ol><li>This is not sustainable</li><li>We cannot live in fear of changing requirements just because our code is becoming too long to read. Every time you are told to add a new service that does x a bit differently, you shrug as you imagine how inherently harder it is about to become to maintain this code.</li><li>Any time there are multiple if statements to handle wildly varying situations, it should be a clear sign that there might be something wrong in the modelling.</li></ol><h3>Definition</h3><p>This is a weird place to put a definition, but I might as well. <a href="https://github.com/mcflyhalf">My friend</a>, a proficient educator, insisted that it was a good idea. What is domain modelling?</p><p><a href="https://github.com/mcflyhalf">The aforementioned friend</a> said: I am currently thinking about the objects in your code map, how well they map to objects in real life, and how easy they are to move around when their real world counterparts move around.</p><p>A definition from <a href="https://study.com/academy/lesson/domain-modeling-definition-examples.html">study.com</a>: It is a model that shows the real-world flow, relationships between entities, and concepts.</p><p>I say: Yes, all the above, but additionally, something that is able to <strong>rise above reality and conquer it</strong>.</p><p>In the cases of complex and convoluted requirements, in government and such, we have layers upon layers of bureaucracy to address situations where a problem is complex.</p><p>“<em>This particular document is normally approved by this department, but because your hair is blue, it’s approved by that other department, which opens on Sundays, between 4am and 5am. You also need 2 signatories because we see here you like to write with pink markers</em>. ”</p><p>Maybe, in this case, the real world should adhere to a software model, and not the reverse. LOL.</p><h3>Refactoring</h3><p>There are various ways of solving the challenge of scaling the maintenance. This is how I’d solve it. Feel free to suggest your own in the comments.</p><p>First of all, when did the rain start to beat us? When should we have caught wind of the issue? I posit that this was when we started writing the many “if” statements. Refactoring is a never ending process. By <strong>Week 4</strong>, we should have started having alarm bells ringing in our head as we wrote the requirements for <strong>Kenya</strong> and <strong>SSB</strong>.</p><p><strong>Shipping Service</strong></p><p>There are some aspects that “belong” to the shipping service. Whether a receipt is to be included in the box is related to the shipping service, not the order. The same applies to priority of the order.</p><p>Therefore shipping service becomes its own class.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/720/1*hE0Z-NkgJTgA2yWIINkw5Q.png" /></figure><p><strong>Country</strong></p><p>As per now, inclusion of a phone number is exclusively based on a country. So we can move the phone requirments there as well.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/469/1*revn1lVlb1NcDnKSgDZhyw.png" /></figure><p>With the above changes, I can create a shipping service, and a country using:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/631/1*eptAebmbyKcwjDFO1PjxQQ.png" /></figure><p>And the ship_order function is this simple.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QaokUMEBiDCHi9UqfWiEmw.png" /></figure><p>As a true test, adding a new shipping service <strong>SSF</strong> with a mix of requirements becomes as easy as:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/644/1*Nn8QRCj0RlXshcYOD1w0wQ.png" /></figure><h3>Conclusion</h3><p>In cleaning up the ship_order function, we have removed <strong>ALMOST ALL</strong> references to “literal” countries and shipping_services, and instead are running the model using the objects. This has made it extensible and easy to read.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ijRt8yBaSEzU-de1loo-Ew.png" /></figure><p>However, we have not solved the issue of <strong>Kenya</strong> and shipping using shipping_service <strong>SSB</strong>. That is something that we shall solve in a subsequent article.</p><p>PS: Depending on how the requirements grow or don’t grow, you may not need to remodel your domain that much. A bunch of if statements thrown all over the place may suffice.</p><p>PS of the PS: Feel free to correct, contribute, suggest changes to any of the ideas provided above. This life is all about learning. :)</p><p><em>The writer is a lead software developer/devops in a small remote team, and also moonlights as a pianist/keyboardist in Nairobi. Nyoro has over 10 years experience with Python, 6 years in full stack and SRE, and 2 years in docker with swarm. The writer is a graduate of Mechatronic Engineering at JKUAT.</em></p><p><em>More content at </em><a href="https://plainenglish.io/"><strong><em>PlainEnglish.io</em></strong></a><em>. Sign up for our </em><a href="http://newsletter.plainenglish.io/"><strong><em>free weekly newsletter</em></strong></a><em>. Follow us on </em><a href="https://twitter.com/inPlainEngHQ"><strong><em>Twitter</em></strong></a>, <a href="https://www.linkedin.com/company/inplainenglish/"><strong><em>LinkedIn</em></strong></a>,<em> and</em><strong><em> </em></strong><a href="https://discord.gg/GtDtUAvyhW"><strong><em>Discord</em></strong></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=56e072bd3c1" width="1" height="1" alt=""><hr><p><a href="https://python.plainenglish.io/domain-modelling-1-a-problem-you-didnt-know-you-have-56e072bd3c1">Domain Modelling 1: A problem you didn’t know you have</a> was originally published in <a href="https://python.plainenglish.io/">Python in Plain English</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>