[{"content":"AI/ML Machine Learning Roadmap - Daniel Bourke Programming Intel NPU 6.034 Artificial Intelligence, Fall 2010 - MIT Made With Machine Learning Course Stanford CS221 - Autumn 2025 Lectures Efficient Memory Management for Large Language Model Serving with PagedAttention Gaudi Architecture — Gaudi Documentation 1.23.0 documentation Algorithms Leetcode Cheatsheet - jwl-7 COS 423, SPRING 2018: Theory Of Algorithms - Kevin Wayne (Princeton University) Blockchain The EVM Handbook Nader’s web3 Resources for Developers Compiler Design Cal State Uni CS 151: Compiler Construction Compiler Explorer - GodBolt Havard Uni CS 153 2019 Fall Programming Well - Abstraction and Design In Computation Computer Networking Computer Networking: A Top-Down Approach Databases CMU - Intro to Database Systems Postgres Is Enough Redis is fast - I\u0026rsquo;ll cache in Postgres DevOps \u0026amp; SRE Google SRE Books - Site Reliability Engineering fundamentals from Google The Phoenix Project - Gene Kim - DevOps principles in novel form The Twelve-Factor App - Methodology for building modern SaaS apps DevOps Roadmap - Step-by-step guide to becoming a DevOps engineer Continuous Delivery - Jez Humble - Reliable software releases through automation LocalhostConf 2023 - limistah\u0026rsquo;s Note Digital Design \u0026amp; Computer Architecture Onur Mutlu - Digital Design and Computer Architecture: 2025 Onur Mutlu - Digital Design and Computer Architecture: 2022 Onur Mutlu - Digital Design and Computer Architecture: 2021 Distributed Systems MIT 6.824: Distributed Systems - Classic MIT course on distributed systems Distributed Systems - Martin Kleppmann - Lecture series from Cambridge Raft Consensus Visualization - Interactive visualization of the Raft algorithm Papers We Love - Distributed Systems - Curated collection of classic papers Designing Data-Intensive Applications - Kleppmann - The definitive guide to building distributed systems Erlang Learn You Some Erlang - The best introduction to Erlang Erlang Documentation - Official - Official Erlang language docs Erlang Factory Videos - Conference talks on Erlang and BEAM The BEAM Book - Deep dive into the Erlang VM Generic Challenging Projects Programmers Should Try A Paradigm Shift in Computer Science? - Tu Wien Informatics Go Effective Go - Official - The definitive style guide from the Go team Go by Example - Hands-on introduction with annotated example programs Ultimate Go - Bill Kennedy - Deep dive into Go internals and design Concurrency Patterns - Rob Pike - Classic talk on Go concurrency GopherCon \u0026lsquo;25 Testing Workshop - Alex Rios Standard Library Walkthrough - In-depth exploration of Go\u0026rsquo;s stdlib Home Lab Home Lab Act Series - David Zmick JavaScript \u0026amp; Node.js JavaScript.info - Modern Tutorial - Comprehensive modern JavaScript tutorial You Don\u0026rsquo;t Know JS - Kyle Simpson - Deep dive into JavaScript mechanics Node.js Best Practices - Collection of Node.js best practices 33 JS Concepts - Leonardo Maldonado - Concepts every JavaScript developer should know V8 Blog - Under the Hood - How JavaScript engines work Kubernetes \u0026amp; Container Orchestration Kubernetes Documentation - Official - Comprehensive official docs for K8s Kubernetes The Hard Way - Kelsey Hightower - Learn K8s internals by setting up a cluster manually KubeCon + CloudNativeCon Talks - Conference talks from the CNCF community Kubernetes Patterns - Red Hat - Design patterns for cloud-native apps CNCF Landscape - Overview of the cloud-native ecosystem Learn Kubernetes Security - Security best practices and resources Life Coping with the Stress of Layoff and Unemployment Is Getting a Masters in CS Worth it Warren Buffett\u0026rsquo;s Final Letter, 2025 The Science of Well-Being - In this course you will engage in a series of challenges designed to increase your own happiness and build more productive habits. As preparation for these t\u0026hellip; Logic Logic Tools Neovim Learn Neovim The Practical Way Open Source Open Source Contribution Guide - limistah’s Note Good First Issues dot Dev Public Speaking CFP Template The Ultimate Guide to Memorable Talks Speaking IO On Conference Speaking - Hynek Ruby Ruby Documentation - Official - Official Ruby language documentation Ruby Style Guide - Community-driven Ruby style guide RubyTapas - Avdi Grimm - Short screencasts on Ruby mastery Practicing Ruby - Gregory Brown - In-depth Ruby programming articles Ruby Weekly Newsletter - Weekly Ruby news and articles Rust The Rust Book - Official - The comprehensive guide to learning Rust Rust by Example - Learn Rust through practical examples Rustlings - Interactive Exercises - Small exercises to get you used to reading and writing Rust Crust of Rust - Jon Gjengset - Intermediate Rust programming topics Awesome Rust - Curated list of Rust libraries and resources Self Help/Learning Teach Yourself CS Open Source Society University - Computer Science System Design System Cheatsheet - bhavul Kafka is fast \u0026ndash; I\u0026rsquo;ll use Postgres Systems Programming Berkeley: CS162 - Operating Systems and Systems Programming PintOS NachOS C \u0026amp; C++ programming notes - Ben Langmead Programming in C and C++ (2024/2025) - University of Cambridge TMux TMux - Getting Started TMux - Cheatsheet Writing Community Writer Programs ","permalink":"https://limistah.dev/links/","summary":"\u003ch3 id=\"aiml\"\u003eAI/ML\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca \n  href=\"https://github.com/mrdbourke/machine-learning-roadmap\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eMachine Learning Roadmap - Daniel Bourke\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://intel.github.io/intel-npu-acceleration-library/npu.html\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eProgramming Intel NPU\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://www.youtube.com/playlist?list=PLUl4u3cNGP63gFHB6xb-kVBiQHYe_4hSi\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003e6.034 Artificial Intelligence, Fall 2010 - MIT\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://madewithml.com/#course\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eMade With Machine Learning Course\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://github.com/stanford-cs221/autumn2025-lectures\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eStanford CS221 - Autumn 2025 Lectures\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://arxiv.org/pdf/2309.06180#page=1.22\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eEfficient Memory Management for Large Language Model Serving with PagedAttention\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://docs.habana.ai/en/latest/Gaudi_Overview/Gaudi_Architecture.html\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eGaudi Architecture — Gaudi Documentation 1.23.0 documentation\u003c/a\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch3 id=\"algorithms\"\u003eAlgorithms\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca \n  href=\"https://jwl-7.github.io/leetcode-cheatsheet/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eLeetcode Cheatsheet - jwl-7\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://www.cs.princeton.edu/~wayne/kleinberg-tardos/pdf/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eCOS 423, SPRING 2018: Theory Of Algorithms - Kevin Wayne (Princeton University)\u003c/a\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch3 id=\"blockchain\"\u003eBlockchain\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca \n  href=\"https://gh05ec.notion.site/The-EVM-Handbook-a4c11d0addec477fb91192561e8d91b9\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eThe EVM Handbook\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://naderdabit.notion.site/Nader-s-web3-Resources-for-Developers-a200ed2ef21c4d578dc158df2b882c63\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eNader’s web3 Resources for Developers\u003c/a\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch3 id=\"compiler-design\"\u003eCompiler Design\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca \n  href=\"https://www.youtube.com/playlist?list=PL6KMWPQP_DM97Hh0PYNgJord-sANFTI3i\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eCal State Uni CS 151: Compiler Construction\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://godbolt.org\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eCompiler Explorer - GodBolt\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://groups.seas.harvard.edu/courses/cs153/2019fa/schedule.html\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eHavard Uni CS 153 2019 Fall\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://book.cs51.io/pdfs/abstraction.pdf\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eProgramming Well - Abstraction and Design In Computation\u003c/a\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch3 id=\"computer-networking\"\u003eComputer Networking\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca \n  href=\"https://www.youtube.com/watch?v=74sEFYBBRAY\u0026amp;list=PLByK_3hwzY3Tysh-SY9MKZhMm9wIfNOas\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eComputer Networking: A Top-Down Approach\u003c/a\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch3 id=\"databases\"\u003eDatabases\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca \n  href=\"https://15445.courses.cs.cmu.edu/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eCMU - Intro to Database Systems\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://gist.github.com/cpursley/c8fb81fe8a7e5df038158bdfe0f06dbb\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003ePostgres Is Enough\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://dizzy.zone/2025/09/24/Redis-is-fast-Ill-cache-in-Postgres/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eRedis is fast - I\u0026rsquo;ll cache in Postgres\u003c/a\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch3 id=\"devops--sre\"\u003eDevOps \u0026amp; SRE\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca \n  href=\"https://sre.google/books/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eGoogle SRE Books\u003c/a\u003e - Site Reliability Engineering fundamentals from Google\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://itrevolution.com/product/the-phoenix-project/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eThe Phoenix Project - Gene Kim\u003c/a\u003e - DevOps principles in novel form\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://12factor.net/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eThe Twelve-Factor App\u003c/a\u003e - Methodology for building modern SaaS apps\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://roadmap.sh/devops\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eDevOps Roadmap\u003c/a\u003e - Step-by-step guide to becoming a DevOps engineer\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://continuousdelivery.com/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eContinuous Delivery - Jez Humble\u003c/a\u003e - Reliable software releases through automation\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://www.notion.so/LocalhostConf-2023-2eb7047631ed4c9b909651d0e63ec3d9?source=copy_link\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eLocalhostConf 2023 - limistah\u0026rsquo;s Note\u003c/a\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch3 id=\"digital-design--computer-architecture\"\u003eDigital Design \u0026amp; Computer Architecture\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca \n  href=\"https://www.youtube.com/watch?v=ubhxKNlOlRg\u0026amp;list=PL5Q2soXY2Zi9Eo29LMgKVcaydS7V1zZW3\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eOnur Mutlu - Digital Design and Computer Architecture: 2025\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://www.youtube.com/watch?v=cpXdE3HwvK0\u0026amp;list=PL5Q2soXY2Zi97Ya5DEUpMpO2bbAoaG7c6\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eOnur Mutlu - Digital Design and Computer Architecture: 2022\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://www.youtube.com/watch?v=LbC0EZY8yw4\u0026amp;list=PL5Q2soXY2Zi_uej3aY39YB5pfW4SJ7LlN\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eOnur Mutlu - Digital Design and Computer Architecture: 2021\u003c/a\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch3 id=\"distributed-systems\"\u003eDistributed Systems\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca \n  href=\"https://www.youtube.com/@6.824\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eMIT 6.824: Distributed Systems\u003c/a\u003e - Classic MIT course on distributed systems\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://www.youtube.com/playlist?list=PLeKd45zvjcDFUEv_ohr_HdUFe97RItdiB\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eDistributed Systems - Martin Kleppmann\u003c/a\u003e - Lecture series from Cambridge\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://raft.github.io/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eRaft Consensus Visualization\u003c/a\u003e - Interactive visualization of the Raft algorithm\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://github.com/papers-we-love/papers-we-love/tree/main/distributed_systems\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003ePapers We Love - Distributed Systems\u003c/a\u003e - Curated collection of classic papers\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://dataintensive.net/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eDesigning Data-Intensive Applications - Kleppmann\u003c/a\u003e - The definitive guide to building distributed systems\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch3 id=\"erlang\"\u003eErlang\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca \n  href=\"https://learnyousomeerlang.com/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eLearn You Some Erlang\u003c/a\u003e - The best introduction to Erlang\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://www.erlang.org/docs\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eErlang Documentation - Official\u003c/a\u003e - Official Erlang language docs\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://www.youtube.com/@ErlangSolutionsLtd\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eErlang Factory Videos\u003c/a\u003e - Conference talks on Erlang and BEAM\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://blog.stenmans.org/theBeamBook/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eThe BEAM Book\u003c/a\u003e - Deep dive into the Erlang VM\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch3 id=\"generic\"\u003eGeneric\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca \n  href=\"https://austinhenley.com/blog/challengingprojects.html\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eChallenging Projects Programmers Should Try\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://www.youtube.com/playlist?list=PLgT6-EN5L0ZX2OeF2m_6AaZvcujQG92_E\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eA Paradigm Shift in Computer Science? - Tu Wien Informatics\u003c/a\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch3 id=\"go\"\u003eGo\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca \n  href=\"https://go.dev/doc/effective_go\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eEffective Go - Official\u003c/a\u003e - The definitive style guide from the Go team\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://gobyexample.com/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eGo by Example\u003c/a\u003e - Hands-on introduction with annotated example programs\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://github.com/ardanlabs/gotraining\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eUltimate Go - Bill Kennedy\u003c/a\u003e - Deep dive into Go internals and design\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://www.youtube.com/watch?v=f6kdp27TYZs\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eConcurrency Patterns - Rob Pike\u003c/a\u003e - Classic talk on Go concurrency\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://github.com/alexrios/testing-workshop\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eGopherCon \u0026lsquo;25 Testing Workshop - Alex Rios\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://medium.com/go-walkthrough\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eStandard Library Walkthrough\u003c/a\u003e - In-depth exploration of Go\u0026rsquo;s stdlib\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch3 id=\"home-lab\"\u003eHome Lab\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca \n  href=\"https://dpzmick.com/posts/2020-01-09-vpn.html\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eHome Lab Act Series - David Zmick\u003c/a\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch3 id=\"javascript--nodejs\"\u003eJavaScript \u0026amp; Node.js\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca \n  href=\"https://javascript.info/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eJavaScript.info - Modern Tutorial\u003c/a\u003e - Comprehensive modern JavaScript tutorial\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://github.com/getify/You-Dont-Know-JS\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eYou Don\u0026rsquo;t Know JS - Kyle Simpson\u003c/a\u003e - Deep dive into JavaScript mechanics\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://github.com/goldbergyoni/nodebestpractices\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eNode.js Best Practices\u003c/a\u003e - Collection of Node.js best practices\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://github.com/leonardomso/33-js-concepts\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003e33 JS Concepts - Leonardo Maldonado\u003c/a\u003e - Concepts every JavaScript developer should know\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://v8.dev/blog\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eV8 Blog - Under the Hood\u003c/a\u003e - How JavaScript engines work\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch3 id=\"kubernetes--container-orchestration\"\u003eKubernetes \u0026amp; Container Orchestration\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca \n  href=\"https://kubernetes.io/docs/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eKubernetes Documentation - Official\u003c/a\u003e - Comprehensive official docs for K8s\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://github.com/kelseyhightower/kubernetes-the-hard-way\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eKubernetes The Hard Way - Kelsey Hightower\u003c/a\u003e - Learn K8s internals by setting up a cluster manually\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://www.youtube.com/@cncf/playlists\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eKubeCon + CloudNativeCon Talks\u003c/a\u003e - Conference talks from the CNCF community\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://www.redhat.com/en/resources/kubernetes-patterns-oreilly-ebook\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eKubernetes Patterns - Red Hat\u003c/a\u003e - Design patterns for cloud-native apps\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://landscape.cncf.io/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eCNCF Landscape\u003c/a\u003e - Overview of the cloud-native ecosystem\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://kubernetes-security.info/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eLearn Kubernetes Security\u003c/a\u003e - Security best practices and resources\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch3 id=\"life\"\u003eLife\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca \n  href=\"https://uhs.berkeley.edu/sites/default/files/coping_with_the_stress_of_layoff_and_unemployment.pdf\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eCoping with the Stress of Layoff and Unemployment\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://sumnerevans.com/posts/school/is-getting-a-masters-in-cs-worth-it/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eIs Getting a Masters in CS Worth it\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://www.berkshirehathaway.com/news/nov1025.pdf\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eWarren Buffett\u0026rsquo;s Final Letter, 2025\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://www.youtube.com/playlist?list=PLWHJHzhtiAuS30X_NfVz37vXfZ-T3XyFK\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eThe Science of Well-Being\u003c/a\u003e - In this course you will engage in a series of challenges designed to increase your own happiness and build more productive habits. As preparation for these t\u0026hellip;\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch3 id=\"logic\"\u003eLogic\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca \n  href=\"https://logictools.org/prop.html\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eLogic Tools\u003c/a\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch3 id=\"neovim\"\u003eNeovim\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca \n  href=\"https://alpha2phi.medium.com/learn-neovim-the-practical-way-8818fcf4830f\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eLearn Neovim The Practical Way\u003c/a\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch3 id=\"open-source\"\u003eOpen Source\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca \n  href=\"https://relic-match-af2.notion.site/Open-Source-Contribution-Guide-Limistah-s-Note-7301aa389fde4fc6b7dcc22f5c746b45\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eOpen Source Contribution Guide - limistah’s Note\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://goodfirstissue.dev\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eGood First Issues dot Dev\u003c/a\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch3 id=\"public-speaking\"\u003ePublic Speaking\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca \n  href=\"https://cfptemplate.com/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eCFP Template\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://nnja.medium.com/the-ultimate-guide-to-memorable-tech-talks-e7c350778d4b\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eThe Ultimate Guide to Memorable Talks\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://speaking.io\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eSpeaking IO\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://hynek.me/articles/speaking/?ref=devrelresourc.es\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eOn Conference Speaking - Hynek \u003c/a\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch3 id=\"ruby\"\u003eRuby\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca \n  href=\"https://ruby-doc.org/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eRuby Documentation - Official\u003c/a\u003e - Official Ruby language documentation\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://rubystyle.guide/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eRuby Style Guide\u003c/a\u003e - Community-driven Ruby style guide\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://www.rubytapas.com/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eRubyTapas - Avdi Grimm\u003c/a\u003e - Short screencasts on Ruby mastery\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://practicingruby.com/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003ePracticing Ruby - Gregory Brown\u003c/a\u003e - In-depth Ruby programming articles\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://rubyweekly.com/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eRuby Weekly Newsletter\u003c/a\u003e - Weekly Ruby news and articles\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch3 id=\"rust\"\u003eRust\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca \n  href=\"https://doc.rust-lang.org/book/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eThe Rust Book - Official\u003c/a\u003e - The comprehensive guide to learning Rust\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://doc.rust-lang.org/rust-by-example/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eRust by Example\u003c/a\u003e - Learn Rust through practical examples\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://github.com/rust-lang/rustlings\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eRustlings - Interactive Exercises\u003c/a\u003e - Small exercises to get you used to reading and writing Rust\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://www.youtube.com/playlist?list=PLqbS7AVVErFiWDOAVrPt7aYmnuuOLYvOa\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eCrust of Rust - Jon Gjengset\u003c/a\u003e - Intermediate Rust programming topics\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://github.com/rust-unofficial/awesome-rust\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eAwesome Rust\u003c/a\u003e - Curated list of Rust libraries and resources\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch3 id=\"self-helplearning\"\u003eSelf Help/Learning\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca \n  href=\"https://teachyourselfcs.com/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eTeach Yourself CS\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://github.com/ossu/computer-science\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eOpen Source Society University - Computer Science\u003c/a\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch3 id=\"system-design\"\u003eSystem Design\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca \n  href=\"https://bhavul.github.io/System-Design-Cheatsheet/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eSystem Cheatsheet - bhavul\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://topicpartition.io/blog/postgres-pubsub-queue-benchmarks\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eKafka is fast \u0026ndash; I\u0026rsquo;ll use Postgres\u003c/a\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch3 id=\"systems-programming\"\u003eSystems Programming\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca \n  href=\"https://www.youtube.com/watch?v=pPzVV2kkGHc\u0026amp;list=PLF2K2xZjNEf97A_uBCwEl61sdxWVP7VWC\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eBerkeley: CS162 - Operating Systems and Systems Programming\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://web.stanford.edu/class/cs140/projects/pintos/pintos_1.html\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003ePintOS\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://homes.cs.washington.edu/~tom/nachos/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eNachOS\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://github.com/BenLangmead/c-cpp-notes\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eC \u0026amp; C++ programming notes - Ben Langmead\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://www.cl.cam.ac.uk/teaching/2425/ProgC/materials.html\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eProgramming in C and C++ (2024/2025) - University of Cambridge\u003c/a\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch3 id=\"tmux\"\u003eTMux\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca \n  href=\"https://github.com/tmux/tmux/wiki/Getting-Started\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eTMux - Getting Started\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://tmuxcheatsheet.com/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eTMux - Cheatsheet\u003c/a\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch3 id=\"writing\"\u003eWriting\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca \n  href=\"https://github.com/malgamves/CommunityWriterPrograms\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eCommunity Writer Programs\u003c/a\u003e\u003c/li\u003e\n\u003c/ol\u003e","title":"Useful Resources"},{"content":"I have witnessed a lot of evolution in technology, but Artificial Intelligence (AI) is just great. As we share the same initials (Aleem Isiaka), I have decided to be hands-on with it.\nOn this page, I will continue to share how I use and explore the technology for work, learning, and normal life. Things I like and how I believe they can be improved.\nSummary Four branches. Almost everything I do with AI fits into one of these.\nDECISION TREE how I decide when to reach for it PRIMARY PATH if (I know what I want and an AI agent can do it) → Structure the implementation. Let the model generate. Revise, review, intervene manually. COMMON if (I know what I want but no AI agent is good enough) → Gather resources. Refine the idea with an LLM. Implement manually. EXPLORATORY if (I don't know what I want) → Prompt the AI for a general direction. Transition into one of the paths above. TRIVIAL if (everyday random search) → Just prompt the AI. At Work Software engineering has seen immense integration of AI into its workflows. For the things I research, decide, and implement — here\u0026rsquo;s the pipeline:\n→ STAGE 01 Research ChatGPT (free) + Gemini Pull as much as possible from Gemini → export to markdown → import to ChatGPT for refinement.\nHeard Grok is good for this. Not tried yet.\n→ STAGE 02 Decide Anthropic (Claude) + Claude web app for design work Access Claude via GitHub Copilot — more economical than direct API. Use the web app when I need to think visually.\nAnthropic's models are the best here.\nSTAGE 03 Implement OpenCode + Copilot + Claude Code (skills) for one-shots Dual-agent setup: Plan mode (Opus 4.5, read-only) → Build mode (Sonnet 4.5, write). Claude Code skills when I need to move fast.\nOutput is a draft. I revise, review, query for reasoning on anything odd.\nResearching I mostly use ChatGPT (free version) for this, and pair it with Gemini. I pull as much information as I can from Gemini, export to a markdown/text file, then import into ChatGPT for a refined result.\nI\u0026rsquo;ve also heard Grok can be great for this, but I haven\u0026rsquo;t tried it yet.\nDeciding Anthropic\u0026rsquo;s models are the best here. Sometimes I merge both research and decision into a single session on Claude Code. I\u0026rsquo;ve also found accessing Claude models via GitHub Copilot to be more economical — more on that in the implementation phase.\nFor anything that touches design — visual exploration, UI mockups, comparing layouts — I use the Claude web app. Being able to render the output in the same window and iterate on it side by side is something I haven\u0026rsquo;t found anywhere else.\nImplementation This largely involves coding. I use OpenCode as my coding agent and connect to Anthropic\u0026rsquo;s models via the GitHub Copilot subscription. I mostly run out of premium requests, but Copilot\u0026rsquo;s pricing model is still reasonable — we have access to Copilot Pro through our organisation at work.\nMy OpenCode Setup I run OpenCode with two distinct agents for different workflows. The dual-agent setup lets me separate thinking from doing — plan thoroughly in read-only mode, then execute with confidence in build mode.\n~/.config/opencode/config.json AGENT_01 READ-ONLY plan github-copilot/claude-opus-4.5 Research, exploration, implementation planning. No file changes. AGENT_02 WRITE build github-copilot/claude-sonnet-4.5 Executes plans, writes code, makes changes. plugins: superpowers wakatime websearch-cited (Gemini 2.5 Flash) I pair Copilot with a Claude Code Pro subscription, but I mostly use that when I have to one-shot a task.\nI take the output of the LLMs as my initial draft, then revise and review to ensure it meets my requirements and coding style. This process still requires me to code — and increases my awareness of the outputs rather than teaching an LLM how I write code. Whenever there\u0026rsquo;s an abnormal output, I query the model for its reasoning — a kind of two-factor verification to avoid slops.\nI also take extra time going over the PRs personally before requesting external reviews.\nI know extreme AI usage results in brain rot, and even LLMs themselves generate slops — which is not really my fear. I care more about my reputation as an engineer. Once a name is tagged to a piece of work, the means stops justifying the end. Responsibility has never been more important.\nVibe Coding When I have to move with speed, I employ Claude Code. When I have to be more cautious — which is most of the time — I plan the task on OpenCode, review and revise until an acceptable implementation plan is reached before proceeding.\nLearning A continuous process, and not so different from work.\nThe AI Mode of Google is, in my opinion, underrated. I post my questions directly to Google and can either go deeper into the links or click the AI Mode tab for the summary. With AI Mode I also get follow-up questions, which has always been a natural step after an initial Google search — instead of starting a new query and losing the original context.\nFor research work I use NotebookLM — also work-related research sometimes. I love that it uses my own curated sources, plus a little internal knowledge, which makes referencing easier and a deep dive more thorough.\nHermes — my private tutor I run Hermes locally — for now. The plan is to move it to a small cloud box so it\u0026rsquo;s reachable from anywhere. The job is narrow and personal: do research for me, help me learn French, sharpen my English, and keep my Arabic from going rusty.\nI paired it with ddts so it generates voice transcription of words — pronunciation drilling without leaving the terminal. Hermes itself routes between two backends: GitHub Copilot for hosted Claude/GPT-class lifts, and Ollama (via Grok) for everything I\u0026rsquo;d rather keep on my own metal.\nLANGUAGE LOOP hermes + ddts · self-hosted INPUT me a word, phrase, or question prompt→ AGENT hermes self-hosted local → cloud (planned) text→ TTS ddts pronunciation, fr / en / ar audio→ OUTPUT me listen, repeat, drill HERMES → MODEL BACKENDS routed by task GitHub Copilot hosted · Claude / GPT class · for the heavy lifts Ollama via Grok local · open weights · drills, private prompts scope: research, languages languages: french · english · arabic data: private prompts stay local Doing it this way means my language practice doesn\u0026rsquo;t leak into a hosted model\u0026rsquo;s memory, and the model stays mine. The cloud move is purely an availability concern — same weights, same scope.\nNormal Life I mostly use ChatGPT for everything else. The GPT-5 models are great enough for everyday tasks. I don\u0026rsquo;t have a subscription — I haven\u0026rsquo;t had a reason to need one, even for their memory feature.\nI tried OpenClaw and its variants (PicoClaw) as a Golang engineer. I don\u0026rsquo;t like the idea of having such agents.\nAI should be what I prompt — not what decides what I can or should prompt.\nI believe on-device models would be the best route to go with LLMs; some projects have started doing that, like Handy. We\u0026rsquo;ll see more of this kind of integration in the future. I use Grammarly and Harper — not sure if they use AI internally, but this is a good way to use it: as a tool, not as a replacement.\nWhat I don\u0026rsquo;t like Because LLMs are large prediction engines, I don\u0026rsquo;t enjoy their output for creative writing — I do this manually. A good example: asking Claude to help me generate a roadmap for a private software engineering mentorship programme. The output was not great. Some would blame my prompting skills; I believe they\u0026rsquo;re not bad at all. Same reason I don\u0026rsquo;t use them to write.\nThe power still lies in having a concise idea of what is expected. LLMs can help bootstrap them — still, they aren\u0026rsquo;t always the best, and no tangible replacement for our brains. Not yet.\nImprovements? Of course!\nUsing hosted models for everyday inference is risky; it\u0026rsquo;s surprising how much ChatGPT already knows about us in all of its existence compared to Google. It makes sense to locally host some open source models if latency requirements aren\u0026rsquo;t strict.\n","permalink":"https://limistah.dev/ai/","summary":"\u003cp\u003eI have witnessed a lot of evolution in technology, but Artificial Intelligence (AI) is just great. As we share the same initials (Aleem Isiaka), I have decided to be hands-on with it.\u003c/p\u003e\n\u003cp\u003eOn this page, I will continue to share how I use and explore the technology for \u003cem\u003ework\u003c/em\u003e, \u003cem\u003elearning\u003c/em\u003e, and \u003cem\u003enormal life\u003c/em\u003e. Things I like and how I believe they can be improved.\u003c/p\u003e\n\u003ch2 id=\"summary\"\u003eSummary\u003c/h2\u003e\n\u003cp\u003eFour branches. Almost everything I do with AI fits into one of these.\u003c/p\u003e","title":"How I Use AI"},{"content":"Motivation The growth of ChatGPT and other transformer-based artificial intelligence tools has been fascinating, especially as they are applied to knowledge work — as they should be.\nIn just a few years, we transitioned from ChatGPT being able to answer arbitrary questions to tab completions in tools like Cursor, and now to agents doing most of the coding work. With this growth, some professions have been threatened; my own field (software engineering) has witnessed hiring freezes for junior developers and reduced hiring for senior engineers.\nAt work we have discussed the job market disruption caused by AI and the resulting developer efficiencies countless times. Some close friends have also requested my stance on the AI era for software engineers. Through all of this, my conclusion has remained the same.\nI will draw most of my points from traditional engineering disciplines. First, they lay the foundation for general engineering. Second, I have extensive training as a mechanical engineer, so bear with me if liquids and metals infuriate you.\nThe Great Pyramids of Egypt Martin Odler\u0026rsquo;s book cover, Old Kingdom Copper Tools and Model Tools\nThe Great Pyramid of Giza was built around 2560 BC and we still don\u0026rsquo;t know exactly how, which tells us knowledge was not documented in any transferable system. It lived in the hands of the master craftsman, passed it down through apprenticeship, refined through failure, and unfortunately buried with the builder. Archaeologists estimate the pyramid required roughly 20,000–30,000 workers at its peak, which is a massive human brain requirement just to move and place rock.\nThis is craft engineering: survival-driven, trial-and-error, deeply personal. It produced extraordinary things but could not survive the death of the craftsman who held it.\nThe earliest programmers operated similarly. In the 1950s, there were perhaps a few hundred people in the world who could write a working program which involves toggling switches on ENIAC, hand-assembling machine instructions, one person per machine. A program worked because that specific person understood a specific memory map. No documentation. No reproducibility. It ran, and that was enough — until that person left.\nThis earlier form of programming proved that large-scale construction and complex computation were achievable after all, same way the Romans would use the pyramids as a model for what organized engineering can accomplish.\nThe Roman Roads By AlMare - Own work, Public Domain\nThe Romans were able to build 400,000 km of roads through better craftsmen — replacing craftsmen with process. Each Roman legion of 5,000–6,000 soldiers carried surveying tools and construction equipment as standard kit. A legion, anywhere in the empire, can build a road segment that will connect cleanly to any other. And this system worked because they were able to encode knowledge into repeatable steps that did not depend on any person.\nThe human brain requirement did not shrink — it shifted. It required fewer master craftsmen, but much more organized laborers that follow a documented procedure. Expertise moved from doing in the case of the pyramids at Giza to standardizing the doing through knowledge encoding.\nSoftware was on the same path in the late 1960s. The 1968 NATO Software Engineering Conference was to address the \u0026ldquo;software crisis\u0026rdquo; — late projects, blown budgets, systems collapsing under their own weight. Here, the term \u0026ldquo;software engineering\u0026rdquo; was coined as a provocation — let\u0026rsquo;s treat this like a real discipline. And after the name, structured programming, specification documents, team hierarchies were introduced.\nAnd by 1980 in the US alone, programmers went from the roughly 10,000 to over 300,000. Also, not because more geniuses appeared, but because process made programming learnable by ordinary trained professionals.\nThese replicable knowledge would lead to theorization of the processes in a future iteration.\nKnowledge Accumulation First editions of Euler\u0026rsquo;s Mechanica, Bernoulli\u0026rsquo;s Hydrodynamica, and Lagrange\u0026rsquo;s Mechanique analytique\nEvery engineering discipline has a moment when theory overtakes intuition. For structural engineering, it was the application of calculus to beam deflection — suddenly you could calculate whether a bridge would hold rather than build it and see.\nThe workforce implications were immediate: a single structural engineer with mathematics could do what previously required a team of empirical trial-and-error builders. Creating a sharp rise for analytic reasoning, over brute-force experimentation.\nSoftware went through the same shift with the formalization of computer science with theories like:\nTuring\u0026rsquo;s computability theory.\nKnuth\u0026rsquo;s algorithm analysis. Type theory\nDijkstra\u0026rsquo;s structured programming proofs.\nThe question shifted again, from \u0026ldquo;does this run on my machine?\u0026rdquo; to \u0026ldquo;is this algorithm provably correct, and at what cost?\u0026rdquo; For the first time, you could reason about software you had never written, on hardware you had never touched.\nBy the 1970s–80s, computer science departments were producing graduates who understood not just how to program but why programs behaved as they did — at a scale no apprenticeship system could match.\nAt this stage, formal theory enables mass production through standardization of manufacturing process and being able to specify, predict, and verify its outputs. Again, this would make the assembly line become possible once a person can calculate material tolerances, failure loads, and production rates in advance.\nMass Production Gas tanks being installed on the new assembly line, late 1913\nHenry Ford did not invent the automobile. He invented a way to build it at a cost ordinary people could afford. On October 7, 1913, the first moving assembly line at Highland Park began with 140 assemblers on a 150-foot chassis line. Assembly time fell from 728 minutes per car to 93.\nBy 1925, the plant employed nearly 70,000 workers who were trained specialists — not master craftsmen, with mastery of one operation and trusted the system to handle the rest. This single change dropped the price of a Model T from $850 to $260.\nThe insight was organizational, not mechanical. A thin layer of process engineers at the top who designed and maintained the system, a small number of all‑knowing master craftsmen, and a much larger group of narrowly skilled operators at the bottom. Total brains required increased, but differently organized — horizontally across many workers rather than vertically inside a few.\nThe 1990s and 2000s were software\u0026rsquo;s assembly line era. Git, object-oriented component architectures, open-source package ecosystems, Agile sprints. The Linux kernel, built by tens of thousands of contributors across every continent, would have been impossible without this organizational stack. By 2010, the global developer population had grown to an estimated 11 million — not because 11 million people understood computation theory, but because the tools and processes had been standardized enough for trained generalists to contribute reliably.\nNow that a large part of production has become a repetitive, automatable work, it becomes economical to build machines that do them through automation.\nAutomation \u0026amp; Optimization industrial robot arrival on automotive lines in the 1960s and 70s, did not eliminate manufacturing jobs, it redistributed them upward.\nA line worker who used to spot-weld became the technician who maintained the welding robot. That technician\u0026rsquo;s supervisor became the process engineer who programmed robot sequences. These required humans to operate at a dramatically higher level of abstraction, drastically reducing the total number of humans required to produce a car. The factory floor now require hundreds of diagnostic and design decisions against thousands of identical repetitive motions.\nSoftware followed identically with DevOps, continuous integration, and cloud infrastructure.\nThings I used to do by hand — provisioning environments, running test suites, coordinating deploys — became pipelines. An engineer that spent days configuring servers now writes a Terraform script that provisions hundreds.\nBy 2022, roughly 27 million software developers existed worldwide — but the fastest-growing roles were not \u0026ldquo;write the code\u0026rdquo; but \u0026ldquo;design the systems, own the architecture, manage the reliability.\u0026rdquo; The execution layer automated; the design layer expanded to absorb the talent.\nThis stage was possible only because mundane tasks have been automated, which frees human cognition from routine execution and concentrates it at the level of system design and optimization — which is precisely the level where AI assistance now operates.\nIntelligent Engineering AI-assisted circuit layout — engineering at the next level of abstraction\nAnd here we are \u0026hellip;\nAI coding agents have automated the translation of well-specified intent into working code — faster than any human team. Continuing the unbroken pattern:\nOnce the current execution layer is automated, and human judgment moves one level up.\nThe answer to \u0026ldquo;where do humans go?\u0026rdquo; has been the same across every prior transition:\nhigher.\nMaybe, some OGs of the industry can make you understand where we are in the history of computer science, software engineering, and human race in general.\nLinus Torvalds Leveraged LLM Torvalds is famously skeptical about AI in the Linux kernel, and calls AI-generated contributions \u0026ldquo;slop.\u0026rdquo;\nIn late 2025, his personal AudioNoise project surfaced on GitHub. He had written the core signal processing in C himself, and for the Python visualization layer — outside his primary domain — he used an LLM. I am not amused because he used AI assisted coding, I am because of his comment:\n\u0026ldquo;Is this much better than I could do by hand? Sure is.\u0026rdquo;1\nThis is the man who in 2000 wrote \u0026ldquo;Talk is cheap. Show me the code\u0026rdquo; — a statement we have used since then. Only for him to merge AI-generated code and acknowledging it outperforms his own hand work twenty-five years later. Not because he cannot write it. Because his judgment is better spent on the part only he can do.\nThat is exactly what happened to the master craftsman when precision machine tools arrived — they did not abandon their expertise, they redirected them upward.\nDon Knuth in \u0026ldquo;Shock! Shock!\u0026rdquo; Donald Knuth is the computer science theorist — has spent sixty-plus years writing The Art of Computer Programming, the mathematical bedrock of the entire industry.\nHe has always been careful about AI: not dismissive, just precise.\nIn February 2026, he published \u0026ldquo;Claude\u0026rsquo;s Cycles\u0026rdquo; — a five-page note with an opening statement:\n\u0026ldquo;Shock! Shock! I learned yesterday that an open problem I\u0026rsquo;d been working on for several weeks had just been solved by Claude Opus 4.6.\u0026rdquo;2\nA colleague fed an unsolved graph theory conjecture — an exercise for a future volume of TAOCP — directly to Claude. In roughly an hour across 31 systematic explorations, the model tried brute-force search, invented \u0026ldquo;serpentine patterns,\u0026rdquo; hit dead ends, pivoted, and found a construction verified correct for every odd number up to 101. Knuth wrote the formal proof himself, then published the paper. His closing line:\nHats off to Claude!2\nI am not surprised that AI solved a hard math problem, but surprised at whose problem, and his conclusions. Knuth did not say the discipline was over. He said he needed to revise his opinions.\nIt also worth noting that Knuth wrote the proof, and published the paper. Emphasizing that the human expert remained essential — for verification, context, and judgment about what mattered.\nWhat changed was where the frontier lay.\nThe Pattern, Summed Up Engineering evolves when existing methods can no longer handle the scale or complexity being demanded of them. Each stage automates the prior bottleneck, formalizes the knowledge that accumulated there, and moves human judgment one level higher — making the next stage possible.\nLook across all six stages and the structure becomes clear:\nBuilding the pyramids required ~20,000 humans to move stone under the direction of a handful of master craftsmen.\nRoman road-building channeled hundreds of thousands of soldiers through documented procedure under a thin layer of engineers.\nFord\u0026rsquo;s assembly line employed 70,000 workers at a single plant, managed and coordinated by process designers.\nModern DevOps teams of dozens manage infrastructure that would have required thousands of manual operators a decade earlier.\nAt each transition, the total cognitive burden did not shrink — it concentrated upward into fewer, more abstractly capable roles.\nThe labor ladder is consistent:\nManual laborers → craftsmen → process engineers → system designers → architects.\nHuman labor does not disappear at any stage. Its nature changes, and the level at which it operates rises. AI continues this sequence by automating the execution layer — translating known solutions into correct syntax, filling boilerplate, fixing well-understood bugs.\nWhat it cannot automate is knowing\nwhat to build, why it matters, whether the requirements are right, and, whether the resulting system is trustworthy. I call these Responsibility - who owns what?\nThose are questions of architecture, domain expertise, and judgment built over years of watching systems fail in unexpected ways. They are not getting easier but harder as systems grow more complex, faster, and more deeply integrated into things that matter.\nThe engineers who understand this will not be replaced. They will be asked to operate one level higher than before — and the engineers who grasp that pattern early will help define what that level looks like.\nSoftware engineering lives on. Not despite the AI revolution, but through it. The pyramid builders are still building. They just have better tools now.\nIf you have thoughts on this — or you\u0026rsquo;re a developer figuring out what the path forward looks like — I\u0026rsquo;d genuinely like to hear from you.\nLinus Torvalds, commit comment on AudioNoise/93a7256, GitHub, 2025.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nDonald E. Knuth, \u0026ldquo;Claude\u0026rsquo;s Cycles,\u0026rdquo; Stanford Computer Science Department, 28 February 2026.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","permalink":"https://limistah.dev/posts/swe-lives-on/","summary":"Since the public release of GPT-3, LLMs have been integrated into many kinds of knowledge work, including software engineering. With the current pace of innovation, will knowledge work become obsolete?","title":"Software Engineering Lives On!"},{"content":"When I was in JSS2, at the back of one of my notebooks, I wrote my name, with three branching lines, each for CEO, President, and a Petroleum Engineer, maybe I was ambitious! At the moment, I am not close to being the kind of CEO I envisioned, and I don’t even have any political affiliation that could probably lead me to becoming a president. But still an engineer, regardless of what I engineer.\nFor someone who has had a fair share of really tough childhoods, that seemed almost impossible, but the mind is the most amazing place I have ever been. It is the place where I can be what I want, do what I want, and live how I want without being judged. And I believe you have your share of wild imaginations. Still, for the mind to imagine, it has to be oblivious of the facts that create physical constraints.\nCreating in the mind can be as manipulative as it can be rewarding. It can lead to a conclusion like this template:\nWhen I achieve ….., then I will be able to act like …….\nOr\nAfter I achieve ….., then I would be able to ……..\nOr\nIf I have X, I can do Y!\nAs we move over the timeline of our lives, we tend to underestimate the things that we could do right now for the later time that we envision happening. Most people that I have met and discussed with are too consumed by the things that affect them right now than the things that they can do to take a step forward.\nIn one of my recent self-reflections, I came to a conclusion that I have seen validated multiple times by different people. Yes, I preach work at high-impact places, and you should ensure that you make this happen to continue winning. Truth be told, it is overwhelming for someone with some kinds of background to achieve this, and this too can lead to conclusions like this:\nWhen I have a Distinction in my degree, I will be able to do… and then I might get into ____ company and then I can take on that career.\nOr like this:\nWhen I move to Lagos, I would be able to do ___, where probably, I could meet ___ and then I can be able to create that biggest brand/business.\nOr\nOnce my Uncle helps to push my CV, I will be able to get the job, and then I can go on to build that career.\nOr\n…\nIt can become endless, and I believe you have your own version of initial assumptions, but the goal of these assumptions is to keep you stuck and unable to proceed, just like a rigid body tries to overcome its inertia. And they(assumptions) all happen in the mind!\nHow to Live Your Life at Full Power — Graham Weaver\nMost times, success stories emphasize a magic moment when they happen. For example, for Osimhen, it could be being picked to play the U-21 World Cup; for Ronaldo, it could be meeting with Sir Ferguson. For Messi, it could be Barca willing to bring him to the academy, but we fail to realize that a lot of previous events have been suppressed.\nFirst, nothing can happen to you without being born, and all the great footballers might have focused their lives on something else if at some point they weren’t introduced to football. Even after the introduction, they might not be enthusiastic about it, or they are enthusiastic but not passionate. This and more are suppressed in success stories.\nSecondly, feeling stuck is normal, even right now, I feel stuck. But doesn’t mean I have to wait for that single event that will unblock me. Rather, I have to ensure I do the decluttering that will allow that single event to occur. It is like picking a particular apple from a basket of fruits; the unneeded ones have to be segregated to get to the particular apple, and it doesn’t mean the segregation efforts are ignored.\nThis is the same thing with everything in life; nothing that we have experienced has ever happened at the speed of light; humans are too slow to achieve such speed.\nWhile I am here, feeling stuck and unable to proceed, is there anything I can do right now that will put me in a better place in the future? Here and Now, there and then? Another of my wild imaginations is how I envision my retirement, if I ever get to it, how it could be, and, more importantly, how the world would be by then. While painting these different scenarios(in my mind), I also ask myself if I am actually doing enough to get me to have the perceived retirement. Here and Now, there and then!\nBut while considering retirement, I have to ensure to ignore existing physical constraints like: how much money I have right now, how much skill I have right now, my network, or even where I live. What I am allowed to imagine are the things that I can do to ensure that retirement works as I would want, and how I get the things that I need.\nWe always forget that we actually were at some point illiterate before we became literate, we were ignorant before we became that genius, we were once taught how to walk, how to take our first sip. Every great and successful person was once at a zero point or even below.\nMany great software engineers became so good first by writing their first line of code on paper, and some great academics that did not see the barrier created by the poor education system in Nigeria, or by athletes with great talent that ignored the lack of equipment or trainers, and entrepreneurs that still went along with their ideas even in harsh and adverse environments like Nigeria.\nHow to Live Your Life at Full Power — Graham Weaver\nWhen stuck, sometimes the only way forward might be to take an uninformed action; the feedback will guide you to either repeat the action or a modified version of it, or to change course completely. This is a quick-and-dirty concept of system analysis, which is why I am biased towards taking action.\nThe thought to end this on: Going from the person you are, with all the known baggage, to the person that you wish to be, do you see a pathway forward, or is it all roadblocks?\n","permalink":"https://limistah.dev/essays/0012-here-and-now-there-and-then/","summary":"Thinking about the future consumes our motivation in the present and keeps us stuck in the past.","title":"Here and Now, There and Then"},{"content":"I recently read the famous advice from Sam Altman, which resonates with me on the choices to make to become successful. I particularly find myself guilty of a few, which might be a reason why I am not as successful as some of the industry leaders – no regrets still. About the same time, the UK Tech Nation rejected my application.\nThrough a deeper reflection, I was led to a stage of asking a fundamental existential question: What do I really want? And I am asking you, what do you really want?\nTo be raised as an African is to live with an acceptance of how things are; not so much change can be effected. And even if changes can happen, they must be carefully done to ensure a balance. Although this has created a very cultured environment, it has also created an environment that misunderstands the concept of time.\nAs children, we were often asked: “What do you want to be in the future?” As an African and a Nigerian in particular, I now believe we all misinterpret this question.\nNow that we are adults each time we are reminded of this question we tend to joke around about how things had not turned out the way we had wanted, how the system itself had not allowed us to be exactly those things, even we blame the innocent JAMB for not giving us the exact score that we needed or a school for offering admission to a course different from what we wanted to study, we blame the economy, we fault the country, we fault the road infrastructure, and even the village people!\nI understand that all of these constraints exist as an African, and I would rather see them as distractions away from the obvious truth. We tend to forget that the question was asked by people who were adults when we were children, and were aware of all the things that were happening back then. The question, I believe, is not what we wanted to be, but rather how we want to spend our greatest resource in life: time. And I will continue to prove it to you as you read along.\nWhile we all claim the system to be exactly as bad as we have found it, it is worth noting that there are grass-to-grace stories even right in our very close circles of very underprivileged individuals in this same environment.\nThere are people that we have witnessed rise to the utmost level, some with unique abilities, most with the same abilities that are generally obtainable, but the only differentiating factor is the amount of time they spent on what got them the results that we celebrate them for which has little to do with the luck, connection, and whatever we attribute their winning strategy to be.\nSam Altman attributed luck to staying long enough till somehow the stars align in our favor, connection is you being at the right place at the right time, skills are just spending enough time doing a graduated task over and over again. All these points point to the concept of giving our attention to a particular thing over a period of time, and asking you again, what do you want?\nThat nerd is not just a bookie; he would graduate with a first class and get that job you thought was impossible for your background.\nThat guy who enjoys organizing communities would go on to organize bigger events in the industry.\nThat guy who ran errands for the lecturers ended up getting the dreaded scholarships, the one who loved to attend events would end up working at top companies with his average grade.\nSome of these guys get these things with an understanding of what they want, while people with profound unawareness do all of these as a result of who they are!\nAt this point, if I ask us What do YOU want WHEN you grow old, I believe I will get an answer similar to what you gave as a child. I want to be called a grandpa, I want to live in a mansion with my grandkids, etc. But that is not the point of the question. Now that you have defined exactly what you want after an X amount of time, would you be willing to pull all your efforts to get you that?\nAfter I got the mail from the UK Immigration office, I reached out to a couple of friends for morale support and to offset my emotions as early as possible. One of the suggestions was knowing what to do next – from my dad, a friend’s suggestion struck me, he said, “If I know this is what I want and I know the things that I need to do to get there, then I will just work on those things instead.”\nIf better put, I’d spend my time wisely to get what I want!\nAt some point in a future post, I will explain what work is and how we have misunderstood it. From what he said, I could understand that he means: I will ensure my time is well spent on getting these things done!\nThis realization is profound for me; it helped me contemplate the question (What do you want to become when you grow older). While I have spent the last few years of my life working as a software engineer, have I worked towards meeting the requirements for the Global Talent visa in particular? Or I was hoping I would cruise through and just arrive at a destination. Same question goes to you, do you think you are doing enough to get those things you are actually hoping for?\nThere is no substitute for the boring, hard work to be done for the result that you yearn for! Aleem Isiaka It is worth mentioning that while there are physical limits on the individual level, we don’t all have the same IQ, we don’t all have the same stamina, and we don’t all have the same height; we all have different realities. But as an individual with enough time to have figured out themselves – thinking you are as old as me, I am putting this question to you: what do you want to have achieved in 10 years? And I am not asking you to dream or create an illusion, I am asking you, after spending 10 years of your time, what do you intend to get back?\nPeople in developed countries understand the notion of time more deeply. I am always fascinated by the trains in Japan. As a Nigerian, traveling from Lagos to Ibadan by road can take 2 hours (a 150km distance), but with Japan’s HighSpeed trains at 300km/hr, it is a 30-minute journey. Meaning I could have an extra 1.5 hours to do whatever I choose to do. Even with this advantage, they live every minute of their life thoroughly accounted for.\nI now believe that we also don’t better understand the statement time is money. We always think of it as the best time spent is that which generates a monetary reward, but it is not.\nWe spend money with the intention of getting something in return, be it tangible or intangible; no one ever spends money without a reason for a reward, and time is not very different. The major difference between a pro league footballer and a division 2 footballer is where they spend their time, ignoring their physical/mental abilities. The difference between an engineer and a doctor is where they spent their time at school - one studied engineering, the other studied how to become a doctor.\nIt should become captivating why the highly successful place more value on their time over anything else. Money can be remade, time spent can not; it is forever gone, money is elastic, time is constant, and has a finite end.\nThe greatest loss we can take is spending our time on anything that does not lead us to a future we hoped for ourselves.\nWhile in Nigeria, the system can be against you, given enough time, I believe you could have become the engineer, doctor, or lawyer that you wanted to be, and even if midway, some realities made us have a change of heart, we could still become that.\nAnd most often, comparing the state of an environment with another environment is a choice of responsibility neglect. We could wish for Nigeria to become like the dream country we want, or hope that the entire continent witnesses a revolution. Still, given a chance in a developed country, we will have a different question: how do we want to live a normal life, if not born into a middle-class family? The Otedolas do not have the same questions that we have; realities are different.\nTo every problem we face, given a different environment, there is an almost similar version of the exact problem.\nThe grass is not always greener on the other side; most times, a stagnant mindset will always question what the environment can do for them instead of what they can do in the environment. There are not enough jobs in the country, but people get jobs; there are not enough healthcare providers in the country, but people get treatments. For whatever we think there is not enough or we lack, there is almost someone with an abundance of it somewhere at that particular moment.\nAnd if the goal is emigrating, time is required to get that result, and the time would be spent in the same environment we termed as unstable. can be seen as investment though!\nThe biggest advantage of money is helping you buy other people’s time, so you can achieve more. This is the sole reason why managers would delegate some tasks rather than do them directly. As humans, we are special and can handle a particular task better than others, such as cooking/plumbing/carpentry. It makes less sense to let you handle your accounting; it is not a good use of your time, pay someone else to do it, except you can’t afford it.\nSo, asking for the last time, what do you really want? – And now – for your time spent on earth?\nIf you can define that, why are you wasting your time doing other things that don’t push you forward towards getting what you want?\nWhy do you keep squandering your time, but put all the blame on the system?\nRemember, whatever you are not changing, you are choosing it.\nIt is mostly not your environment; people excel in it, it is you who can’t define what you want, and if you can, it is you who does not spend enough time on things that can take you where you hope you want.\n","permalink":"https://limistah.dev/essays/0011-define-what-you-want/","summary":"Looking deeper into ourselves, do we even know what we want? And if we do, are we sure we are going to get it?","title":"Define what you want"},{"content":"Motivation Just like in many other programming languages, the easiest and most re-invented program is the hello world program. This post shows how to create an Hello World program in Rust.\nThe main fn Rust like its pairs uses an entry point for all of its programs, this is the code that executes first before any other when a Rust program is executed.\nFunction in Rust A function statement in Rust has the below structure:\n1 2 3 fn name() { // Change The World! } It starts with the fn keyword followed by the name for the function and a parantheses to take the arguments for the function then a pair of braces for the block of code that should run whenever the function is called/invoked.\nThe main fn Rust like its pairs uses an entry point for all of its programs, this is the code that runs first before any other when a Rust program is executed.\nThe main function declaration looks like this:\n1 2 3 fn main() { // Some awesome code } Printing to standard output The hello world program needs to print something to the screen, Rust provides an inbuilt function called println that handles this I/O operation. Its duty is to take whatever value is passed to it and send to the standard output, which is mostly the terminal that the program is running on.\nBringing it together Having an entry point for the program, also, a function to output to standard output, a useful program can be built.\nHence, a hello world program\n1 2 3 fn main() { println!(\u0026#34;hello world!\u0026#34;); } *Ignore the ! after the println statement for now.\nSave the code in a file named main.rs then compile using the rustc tool. If you might need to install it.\n1 rustc main.rs This will produce an executable binary that can be executed by typing this in the command line.\n1 ./main And it should output:\n1 \u0026gt; hello world Phew!!!\n","permalink":"https://limistah.dev/posts/rust-hello-world/","summary":"The basic Rust program that can ever exist","title":"Rust - Hello World"},{"content":"Motivation Aside binary search, two pointer is an algorithm solving technique that can come in handy for some easy to medium problems. While they both look similar from a distance, which in fact prefix sum is – it derives its implementation from two pointers, I am curious to know when to use either of these techniques to solve an algorithm problem.\nTwo Pointers A problem Given a string of characters \u0026ldquo;apple\u0026rdquo; return the longest unique substring,\nHow would you solve this?\nA naive solution A naive solution can be achieved in $O(n^2)$, where an outer array keeps track of the start string and inner array to move a needle across the string, while checking for unique characters within the bound of the index of the outer array and the inner array.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def find_longest_sub_str(s: str): n = len(s) res = 0 for start in range(n): for end in range(n): sub = s[start : end + 1] if len(set(sub)) == len(sub): res = max(res, end + 1 - start) return res # str = a p p l e # lp1 = 0 # lp2 = 1 # longest = maximum(longest, lp2 + (1 - lp1)) # longest = maximum(0, 1 + (1 - 0) ) Breaking down the naive solution, there are four useful parts.\nThe moving start pointer The moving end pointer The working data (which is the subarray from start to end $s[ start : end + 1]$) The solution determination With all this information, is there a way to make this algorithm run faster say in $O(n)$ against the $O(n^2)$?\nAn optimal solution Of course there is, and it should be obvious, we want to have a single loop that can control the two indices.\nRecall the template of binary search:\n1 2 3 4 5 6 7 8 9 10 11 12 def binary_search(arr, target): l = 0 r = len(arr) - 1 while l \u0026lt; r: mid = (l + r) // 2 if arr[mid] == target: return mid elif arr[mid] \u0026gt; target: l = mid + 1 else: r = mid - 1 return -1 An $O(n)$ solution can be derived using it as an inspiration.\nThe first pointer Firstly having a loop that keeps track of the indices starting from $0$, as in the naive solution case. Also, the loop goes through the length of the array – for binary search it only checks if the left has not crossed over to the right.\nSomething like this can be derived\n1 2 3 4 def optimal_sol(s: str): for i in range(len(str)): pass return 0 The right pointer The second index will behave differently, it should only move when a condition is met and that is when there is a controllable range that creates a subarray. The index has to be initialized outside of the loop having also starting at $0$, and maintained by the loop body itself.\n1 2 3 4 5 6 7 def optimal_sol(s: str): i = 0 for j in range(len(str)): # if condition is met: # i += 1 pass return 0 Rather than returning $0$, a variable is used to hold the result, res, and this is also updated only when the condition is met.\n1 2 3 4 5 6 7 8 9 def optimal_sol(s: str): i = 0 res = 0 for j in range(len(str)): # if condition is met: # i += 1 # res = some value pass return res At this point, the termination property of an algorithm is met – the for loop will go through the length of the string.\nThe two pointers And that is a two pointer solution!\n1 2 3 4 5 6 7 8 9 10 a p p l e 0 l,r 1 l r 2 l r 3 l r 4 l r In the illustration above, notice how all the criteria for the naive solution have been met\nA moving start pointer (the left in our case) A moving end pointer (the right in our case) The working data ($s[l:r]$) Solution determination (yet to be implemented) Moving the left pointer There is a solution only when there exists no character in the string $s[l:r]$, a dictionary can be useful in this case.\n1 2 3 4 5 from collections import defaultdict occurrence = defaultdict() occurrence[char] += 1 In the above code, once a char exists in the dictionary its value is incremented by $1$ to track its occurrence.\n1 2 3 4 5 6 7 8 9 10 11 12 13 from collections import defaultdict occurrence = defaultdict() def optimal_sol(s: str): i = 0 for j in range(len(str)): char = s[j] occurrence[char] += 1 # if condition is met: # i += 1 pass return 0 With a mechanism to know immediately if a character exists twice in the string, the left pointer can advance forward.\n1 2 3 4 5 6 7 8 9 10 11 12 from collections import defaultdict def optimal_sol(s: str): res = i = 0 occurrence = defaultdict() for j in range(len(str)): char = s[j] occurrence[char] += 1 if occurrence[char] \u0026gt; 1: i += 1 pass return res And this is what will happen:\n1 2 3 4 5 6 7 idx i j char dict ----------------------------------------------- 0 0 0 a { a: 1 } 1 1 0 p { a: 1, p: 1 } 2 2 1 p { a: 1, p: 2 } 3 3 1 l { a: 1, p: 2, l: 1 } 4 4 1 e { a: 1, p: 2, l: 1, e: 1 } More textually\n1 2 3 4 5 6 7 8 9 10 a p p l e 0 l,r 1 l r 2 l r 3 l r 4 l r Determining a solution Taking the difference of the right and left and add $1$ - due to $zero$ start point, produce a value close to the correct answer:\n( 4 - 1 ) + 1 = 4 But the longest unique substring is $3$, could be one of ap, ple.\nThere has to be an improvement to the solution determination part of the code\nThe only point when a char exists twice is when the occurrence value for the char becomes $2$. And that is the perfect time reset the value back to $1$ and move the left pointer forward by $1$ as well.\nThis accurately tracks the visited values, and assume the substring $s[left:right]$ is unique as all the characters in the counter have $1$ as their value.\nSomething like this:\n1 2 3 if occurrence[char] \u0026gt; 1: occurrence[s[l]] -= 1 l += 1 Bringing it into our solution:\n1 2 3 4 5 6 7 8 9 10 11 12 13 from collections import defaultdict def optimal_sol(s: str): res = i = 0 occurrence = defaultdict() for j in range(len(str)): char = s[j] occurrence[char] += 1 if occurrence[char] \u0026gt; 1: occurrence[s[i]] -= 1 i += 1 pass return res 1 2 3 4 5 6 7 idx i j char dict ----------------------------------------------- 0 0 0 a { a: 1 } 1 1 1 p { a: 1, p: 1 } 2 2 2 p { a: 0, p: 1 } 3 2 3 l { a: 0, p: 1, l: 1 } 4 2 4 e { a: 0, p: 1, l: 1, e: 1 } This works, but will fail if we model it against abbcab, with unique substrings ab, bca, cab.\n1 2 3 4 5 6 7 8 idx i j char dict ----------------------------------------------- 0 0 0 a { a: 1 } 1 0 1 b { a: 1, b: 1 } 2 1 2 b { a: 1, b: 1 } 3 1 3 c { a: 1, b: 1, c: 1 } 4 2 4 a { a: 1, b: 1, c: 1 } 4 3 4 b { a: 1, b: 1, c: 1 } Notice the solution at this point only ensure that char exists in the dictionary, but what is required is that only unique consecutive characters should have values in the dictionary.\nA good improvement will be to move the left pointer forward consecutively until there is no double occurrence - meeting the criteria for a correct solution.\nSo, instead of an if, a while is used. This way, the left keeps moving forward, until all keys in the occurrence have a value of $0$ for every character that the left has visited:\n1 2 3 4 5 6 7 8 9 10 11 12 from collections import defaultdict def optimal_sol(s: str): res = i = 0 occurrence = defaultdict() for j in range(len(str)): char = s[j] occurrence[char] += 1 while occurrence[char] \u0026gt; 1: occurrence[s[i]] -= 1 i += 1 return res And remodeling abbcab using the improvement:\n1 2 3 4 5 6 7 8 9 idx i j char dict ----------------------------------------------- 0 0 0 a { a: 1 } # a 1 0 1 b { a: 1, b: 1 } # ab 2 2 2 b { a: 0, b: 0 } # resets the counters for s[0:2] 2 2 3 b { a: 0, b: 1 } # b 3 2 4 c { a: 0, b: 1, c: 1 } # bc 4 2 4 a { a: 1, b: 1, c: 1 } # bca 4 4 4 b { a: 1, b: 1, c: 1 } # resets the counter for s[2:3], cab Perfect!\nAnd the solution is always the far right index minus the left index plus $1$ - zero index array\n1 res = j - i + 1 Not forgetting the question asked for the longest substring, the correct answer should always be between a max of the current range and a previously known maximum range.\n1 res = max(res, j - i + 1) And plugging it together\n1 2 3 4 5 6 7 8 9 10 11 12 13 from collections import defaultdict def optimal_sol(s: str): res = i = 0 occurrence = defaultdict() for j in range(len(str)): char = s[j] occurrence[char] += 1 while occurrence[char] \u0026gt; 1: occurrence[s[i]] -= 1 i += 1 res = max(res, j - i + 1) return res Analysis The time complexity of this algorithm is $O(n)$, even though there is a while loop for each iteration – which is $O(k)$ for $k$ being the maximum occurrence of a char and can be ignored.\nNote on Two Pointers The idea of two pointers is to have a reference to two indices, get the items within these indices and determine if there is a solution. Similar to divide and conquer? Maybe.\nTwo pointers can only be used for a handful of problems:\nThe longest substring with unique characters The largest sum of a subarray of with lenght $k$ the length of the shortest subarray whose sum is less than a target All these are still following the templates of the naive solution\nHow about Prefix Sums?\nPrefix Sum Just as we did with two pointers. Given a problem statement, how easily can we solve it?\nGiven an array of integers and an integer target, find a subarray that sums to target and return the start and end indices of the subarray\nInput: arr: 1 -20 -3 30 5 4 target: 7\nOutput: 1 4\nSolving this with a naive algorithm:\n1 2 3 4 5 6 7 8 9 def findSubarray(arr, target): n = len(arr) for i in range(n): total = 0 for j in range(i, n): total += arr[j] if total == target: return [i, j] return [-1, -1] This almost looks like the naive two pointer solution earlier.\nIt has two indices, a tracker for the solution which restarts, and a way to determine the solution. This is a solution in $O(n^2)$, we could achieve $O(n)$ runtime by following the similar approach done for the two pointer optimal solution.\n","permalink":"https://limistah.dev/posts/two-pointers-and-prefix-sum/","summary":"Two pointers and Prefix sum are two algorithm solving techniques. In this post, I dissect their differences from their similarities.","title":"Two Pointers \u0026 Prefix Sum"},{"content":"The rustup website has robust installation methods, especially when you are on a Windows OS – this is what worked for me.\nRun the command below in your terminal:\n1 curl --proto \u0026#39;=https\u0026#39; --tlsv1.2 -sSf https://sh.rustup.rs | sh This downloads the installer, then proceeds to ask if you want a customization of the installation, then downloads and installs the below components:\ncargo clippy rust-docs rust-sdt rustc rustfmt Then run one of these to add the binaries to the PATH of the current shell session:\n1 2 3 . \u0026#34;$HOME/.cargo/env\u0026#34; # For sh/bash/zsh/ash/dash/pdksh source \u0026#34;$HOME/.cargo/env.fish\u0026#34; # For fish source $\u0026#34;($nu.home-path)/.cargo/env.nu\u0026#34; # For nushell The installation can be verified by running rustc command, which returns the help output.\nUseful things that can be done with this installation include:\nrustc -version: Get the installation version number rustup update: Update the current installation to the LTS rustup self uninstall: Uninstalls Rust and all the other components installed along it Voila!\n","permalink":"https://limistah.dev/posts/rust-installation/","summary":"Install Rust and Cargo package manager on a system","title":"Rust Installation"},{"content":"Books Algorithms Introduction to Algorithms Concrete Mathematics Cracking The Coding Interview C The C Programming Language Go Learning Go Python How to Think Like a Computer Scientist Rust The Rust Programming Language Assembly Assembly Language for x86 Processor System Design Designing Data Intensive Applications Databases Database Internals Journals Distributed Systems In Search of an Understandable Consensus Algorithm ","permalink":"https://limistah.dev/readings/","summary":"\u003ch2 id=\"books\"\u003eBooks\u003c/h2\u003e\n\u003ch4 id=\"algorithms\"\u003eAlgorithms\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca \n  href=\"/books/introduction-to-algorithms\"\n  \u003eIntroduction to Algorithms\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"/books/assembly-language\"\n  \u003eConcrete Mathematics\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"/books/cracking-the-coding-interview\"\n  \u003eCracking The Coding Interview\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch4 id=\"c\"\u003eC\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca \n  href=\"/books/c-programming-language\"\n  \u003eThe C Programming Language\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch4 id=\"go\"\u003eGo\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca \n  href=\"/books/learning-go\"\n  \u003eLearning Go\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch4 id=\"python\"\u003ePython\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca \n  href=\"/books/python-think-scientist\"\n  \u003eHow to Think Like a Computer Scientist\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch4 id=\"rust\"\u003eRust\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca \n  href=\"/books/the-rust-programming-language\"\n  \u003eThe Rust Programming Language\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch4 id=\"assembly\"\u003eAssembly\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca \n  href=\"/books/assembly-language\"\n  \u003eAssembly Language for x86 Processor\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch4 id=\"system-design\"\u003eSystem Design\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca \n  href=\"/books/designing-data-intensive-application\"\n  \u003eDesigning Data Intensive Applications\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch4 id=\"databases\"\u003eDatabases\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca \n  href=\"/books/database-internals\"\n  \u003eDatabase Internals\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"journals\"\u003eJournals\u003c/h2\u003e\n\u003ch4 id=\"distributed-systems\"\u003eDistributed Systems\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca \n  href=\"/journals/raft\"\n  \u003eIn Search of an Understandable Consensus Algorithm\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e","title":"Public Library"},{"content":"What is Raft? Raft is a consensus algorithm that succeeds Paxos. They both tried to answer a question: how can two different servers run the same program, and at a point in time, both of them, when queried, would return the same data, even if they had received different instructions. For example, suppose server A receives a write operation with value 1 and a later write instruction with value 2 at some point in time. In that case, we should query server B to retrieve the value 2, without the write operation having occurred on server B.\nThis is a classic problem in distributed systems and has been used outside of Consul (the KV store used by Vault) in systems like etcd, the KV store used by Kubernetes, Kafka, CockroachDB, and many more.\nAlternatives to Raft Aside from Raft, there are other consensus algorithms, including notable mentions such as Paxos, Practical Byzantine Fault Tolerance (PBFT), and Zab, among many others. All of them have the same goal in mind as Raft but differ in implementation, hence their differences:\nPaxos: Very reliable but complex; a single bug/mistake can become increasingly difficult to fix.\nPractical Byzantine Fault Tolerance: Not in the same domain as Raft, solves data consistency, while Raft handles crashes and failures\nView stamped Replication: Contains individual sub-protocols, creating more moving parts that can go faulty.\nZookeeper Atomic Broadcast: It can become a performance bottleneck as the server grows larger, is not as simple as Raft, and is not as self-contained as Raft.\nThe Raft Paper For this to be successful, I will strictly follow through with the raft paper, which outlines the required components and their corresponding responsibilities.\nRaft Components The primary component of a raft cluster is the servers that comprise the cluster. A raft server can either be a leader or a follower at any point in time. The type of server determines its responsibilities, which we will explore in the next section.\nRaft Leader A raft leader is, as the name says, a leader! Its sole responsibility is to receive requests, handle them, and propagate the requests to the other servers (followers) in the cluster. In contrast to other consensus algorithms, Raft allows any server to become a leader. This ensures the cluster is always available by removing the bottleneck of always having the same request handler across the cluster.\nRaft Follower A raft follower can accept requests from clients, but it has to proxy the request back to the leader for proper handling. The sole purpose of a raft follower is to replicate the data of the leader. Once a leader handles a request, they have a second responsibility of sending those requests to all their followers. This process ensures that any follower can become a leader without being behind.\nState Machine or Logs For each server in a cluster, there is a log component that it uses to track the order of operations. Once a leader receives a request, it tries to replicate the request to the different followers, and only commits it to its log if the majority of the followers have committed the request to their logs.\nDue to the delicacy of the log, various operations are supported by a raft log. We will discuss this when we explore the raft log in detail.\nOperations Raft supports various operations across its different components.\nHandling client requests ","permalink":"https://limistah.dev/posts/raft-part-1/","summary":"What is Raft, what are the alternatives, and why even build Raft? How are we going to build it?","title":"Raft Protocol: Part 1"},{"content":"VIM is a household name in the Unix world. To most, it is their primary text editor, used for day-to-day editing tasks regardless of complexity. I am starting a series that explores VIM and its uses, as well as how I utilise VIM in my development environment. However, we will begin with the installation.\nFor most Unix installations, vi should already be pre-installed. You can verify this by running vi --version. In some cases, vi has been symlinked to the vim command and can return the help of VIM instead of vi, indicating that Vim is already installed.\nWe can also verify if Vim is installed by running vim --version, which should show a result like this:\nIn that case, we no longer have to worry about the installation.\nIn any case, if you encounter a command not found error for either vi or vim, proceed to the next section.\nInstallation guides Mac OS Install VIM via Homebrew:\n1 brew install Vim Linux Debian Use the apt repository for installation.\n1 2 apt-get update \u0026amp;\u0026amp; apt-get upgrade -y apt-get install vim RHEL / CentOS 1 2 3 4 5 yum update -y yum add Vim # or via dnf dnf search vim dnf install vim-enhanced Alpine\n1 2 3 apk update apk search vim apk add vim vim-doc vim-tutor Verifying Installation After running the correct installation command for your environment, use the version command to verify Vim can respond appropriately:\n1 vim --version First VIM edit To edit any file or create a new one if it doesn\u0026rsquo;t exist. Run vim filename and type what you want to put in the file. When you are done, press the ESC key on your keyboard and type :wq . Then, press ENTER to save and exit the Vim window.\nThe filename file should now contain whatever you typed before pressing the ESC button.\nVerify this by running cat filename.\nThat is it!\nCiao\n","permalink":"https://limistah.dev/posts/vim-101-0-installation/","summary":"This episode of the VIM series explores the different ways of setting up Vim on different environments.","title":"VIM 101: Installation"},{"content":"Motivation I recently had to switch from my previous Mac (Apple M1 Pro) to a newer Mac (Apple M4 Pro), an experience that should have required effort and a careful migration that happened in the least possible time, while I still got access to the same dev experience as I used to on my previous Mac.\nIn this post, I will share how I did it - it is not trivial. The tools I use to make management hassle-free are available for you to explore, and I hope you can find one or two ideas to implement your unique solutions. I will share what did not go well and how I plan to tackle it with my next device switch.\nDevelopment Environment When a developer mentions a machine, they refer to the location where most of their development work is conducted. While we develop on these machines, building the best experience for our application users, we also overlook our own experiences in these places.\nI am obsessed with optimisation, seeking even the slightest change that can bring out a possible 0.01% improvement. It can be as simple as a keystroke, or as complex as how I organize my desktop, or even switching from a less efficient tool to a more effective one. Experienced developers should also experience this frustration - I\u0026rsquo;m paid for how quickly I can accomplish tasks.\nHere is a list of my current development environment tools:\nEditor: A mix of nvim, vscode Browser: Chrome Terminal: Ghostty Shell: zsh Dot File manager: Stow One thing is prevalent with all of them: they are configurable.\nKeeping in Sync Considering that the UNIX environment is configurable through files, we can use a native tool called Stow to manage it.\nStow keeps two folders in sync by creating a symbolic link in the destination folder that points to the originating folder. As an example, we can use Stow to manage our host files(/etc/hosts) or nameserver file(/etc/resolv.conf) or even an entire folder, nginx user-defined server configuration files /etc/nginx/conf.d as an example.\nIn my case, I want to use Stow to manage my user home directory (~/) and the \u0026lsquo;.config\u0026rsquo; directory (~/.config) inside my home directory, which is where my environment configurations will reside.\nWhat should be in sync In my home directory, I have my bash_profile, .zshrc, and .bashrc files, as well as my vim and nvim configurations managed. And inside the .config directory, I want Stow to manage all the folders and files that will be generated and stored there, as most tools use the same folder to manage their configuration against the home directory.\nSetting up Stow Download stow using your package manager:\n1 2 3 4 5 6 7 8 9 10 11 # macos brew install Stow # debian sudo apt-get update sudo apt-get install stow # CentOS/RHEL 7 sudo yum install Stow # or for RHEL 8+ or Fedora sudo dnf install Stow Verify your installation via:\n1 stow --help 1 2 3 4 5 6 7 8 9 stow (GNU Stow) version 2.4.1 SYNOPSIS: stow [OPTION ...] [-D|-S|-R] PACKAGE ... [-D|-S|-R] PACKAGE ... ... ... ... General help using GNU software: \u0026lt;http://www.gnu.org/gethelp/\u0026gt; Configuring and Running Stow I have a folder called dotfiles, and created a new file called .stowrc that contains:\n1 2 3 4 --target=~/.config --ignore=.stowrc --ignore=DS_Store --ignore=atuin/* In my .stowrc, I have configured it to copy all of the files inside of the current directory into my ~/(home) directory and applied it by running:\n1 stow . Tracking with Git To ensure that I don\u0026rsquo;t lose these configurations, I set up Git to track the files, which provides the longevity and ease of synchronization.\nOne issue I faced was that zsh stores its code inside ~/.config/zsh/.oh-my-zsh. The solution that works is to use it as a submodule, which ensures that the currently known zsh repo commit is sufficiently tracked.\nTo start, init git submodule:\n1 git submodule init This creates a .gitmodules file, which can contain:\n1 2 3 [submodule \u0026#34;.config/zsh/ohmyzsh\u0026#34;] path = .config/zsh/ohmyzsh url = https://github.com/ohmyzsh/ohmyzsh.git In this config, I have pointed the zsh repository to .config/zsh/ohmyzsh, and finally, it is initialized by:\n1 git submodule init .config/zsh/ohmyzsh When I need to update the Zsh repository, I run this command.\n1 git submodule update --recursive Great!!!\nMoving to a new Machine For sensitive configs like my ssh keys, I keep track of them manually in a physical drive. This makes it easier to connect to servers and services without having to reconfigure new SSH keys on those services – how will I get access to the service in the first place? Becoming a chicken-and-egg problem.\nTo set up my new machine, I move my SSH keys to the ~/.ssh folder of the new machine and run git clone https://github.com/limistah/dotfiles.git to pull the code directly onto my machine.\nThen I go over the installation for Stow, and finally, I cd into my dotfiles folder, and run stow . to sync the dotfiles with my home directory. Easy!\nWhat did not go well\u0026hellip; Not everything can be managed with Stow, and these are tools that don\u0026rsquo;t have a known configuration file/directory.\nI also had to reinstall my software, including VS Code, Cursor, Notion, and other third-party applications. I did not optimize for this, and it may be an area I can investigate in my next iteration.\nAlso, my work folder had to be manually migrated; it was a lot of pain as the .git, cache, and package manager had to be migrated - imagine a .git folder for a very busy repository, how large can that be? I suggest putting them on a dedicated local server and sharing the folder over the local network. Still, the fear of working from a remote location prevents me from exploring this idea.\nWas it worth it? As easy as this sounds, it is hard, and this is something I\u0026rsquo;ve solved before when I switched to M1 as my primary development machine.\nAnd yes, it was worth it!\nMy keystrokes, aliases, shell, extensions, and other configurations that I might have forgotten could have been lost during the migration, making my life easier. I can retrieve the applications that use these configurations, but enabling the correct configuration for the apps that match my previous environment may not be possible.\nFor that, yes, it was worth it!\n\u0026hellip;Good bye\u0026hellip;\n","permalink":"https://limistah.dev/posts/machine-configuration-stow/","summary":"Getting Unix machine configuration synchronized across multiple devices and a faster way to switch devices while maintaining the same experience.","title":"Managing Machine Configuration with Stow"},{"content":"Towards the end of my NYSC, I had a call with a former coursemate, during which we discussed life after service. He said, “If I get something tangible here, I might just remain in my deployment state.” I responded, “Well, if people have started from zero in Lagos and made it, I don’t see why our stories should be different.”\nThat same year, I stumbled on a book that shifted my mindset permanently — “Mindset” by Carol Dweck. One powerful idea stayed with me: rejection doesn’t always reflect failure. If you did everything right and still didn’t get rewarded, maybe the judge, the spectators, or even the employer got it wrong.\nFor some people like me, with zero connections and stereotyped backgrounds(education, environment, people), following the money can be like completing a puzzle in the dark. One of the big questions is: “How can I get to a place of impact where money flows through, given my uniqueness (not disadvantages)?”\nBetter put:\nThese things are hard. How can I do it? A simple answer, ”it is not rocket science” - Raji Fashola’s famous response.\nI recall that when I was younger, many were considered to have a brighter future solely based on their educational abilities. I will argue that this is not the case. Similarly, there are people with such promises, based on their uniqueness and inbuilt skills, who are meant to lead their peers regardless of the situation. I have found this to be wrong as well.\nSuccess is not a combination of just one single element, as famously said, most successful people are T-skilled. Exceptionally versed in one thing and mostly average in others.\nIf you have a great voice, not having a good appearance can hinder your chances of becoming a successful artist. Having the height but not the body build or stamina can reduce your chances of being drafted by a basketball team. Knowing math is not enough if it can’t be applied, and the results of the application are not effectively communicated.\nOne might think the story of Agba Baller (Asisat Oshoala) or the sprinter Tobi Amusan is a groundbreaking success story, but what we often fail to realize is that their accomplishments, as we know them, were merely them succeeding again. When success is repeated enough, the result is compounded. They did not just start winning at the stage at which we first knew them; they had already won other stages prior. If we had known them from the supposed start, they had succeeded against self-doubt.\nUnbeknownst to most of us, we become the immediate seniors we aspire to be - Aleem Isiaka\nDuring my NYSC programme, I was still a rookie software engineer, fresh out of school with no corporate experience, but I noticed something. Before completing my mechanical engineering program at Laspotech, I worked with one of the industry\u0026rsquo;s senior professionals, my mentor, and we successfully developed a content management system, similar to WordPress, for a prominent state government in Nigeria.\nWhile working with him, he would narrate the technologies in the late 90s and early 20s, ensuring that I realize the privilege I had with the significant advancement in the very short time(imagine not having git, or GitHub, but code living in Floppy Disks(Diskettes), no SSD), omo… x10^100 1. He would often say to me “you should have a better career than I have, as you are starting with way more than I had.” These discussions made me realize we are all on a conveyor belt in this thing called life.\nWhile growing up in the early 20s, my dad would narrate how the Apapa Port had lost its glory days. The European Quarters, a 30-minute walk from our home, was a multicultural “big man” settlement, and he would share stories of Folawiyo Towers as we gazed through its lights in the evenings on our way home from the madrasah - Arabic school.\nHe would share with me memories of how easy it was as a youth to take a night stroll at the Marina in the Lagos Island. When he took us to the beach, he shared stories about the racecourse, what it was meant for, and narrated the games that had happened at the Onikan Stadium. I was still a child at this point, around 2005-2006.\nMy Dad would continue with the stories of how his father moved around the southwestern part of Nigeria as a farmer. How his childhood shaped who he became, the lessons he learnt growing up, and how we can navigate similar experiences if we encounter them. Unknowingly to him, all that I realized at that point was a boy who has grown to be a man - now I am the man, married, while my dad is old with grandchildren.\nJust as my mentor shared his early career with me, so did my father, and the same was true of my many teachers; everyone I knew was once what I was or a little bit different(an innocent child). I followed my path, which is similar to some of theirs. They all grew to become something else that I didn’t know, and now narrate how they did what I am currently doing - adulthood! I think I have to infer old age, as they might be dead by that time.\nIt’s all a manufacturing line, with a conveyor belt transferring us to the next processing stage based on the qualities we\u0026rsquo;ve garnered from the previous stages.\nA Yoruba adage says:\n“You don’t become a (specific)height just by growing taller in a single day.”\nMy mentor is now a Manager at a major tech-enabled company in Nigeria, and any company in the world would be fortunate to have him on board. My dad has grown older and is now the Imam of his entire street. And me, following their paths, just where they were at my current stage, with a bit of steeze and a lot of cappings.\nAfter my NYSC, I chose who I wanted to fit their shoes once they no longer fit them based on my disadvantages (qualities). And all I have been doing is more of what they did exactly to get to where they are; it\u0026rsquo;s not rocket science. I don’t have to end up at their exact position or company; something similar is very okay.\nAll these are emphases; there is so much to be done from a humble place. Starting small doesn’t always mean ending small. How would you have become a manager at NNPCL if you had not worked closely with the kind of people you were to manage? Even if not at NNPCL, how can you be a supervisor if you have not been supervised or done something related? Mind you, some people are naturally born managers, their uniqueness, aye!\nInternships are a great way to learn how an industry operates; they let you make mistakes and give you experienced people at arm’s length. Some paid positions are similar to paid internships. Still, without a supervisor or senior, you are solely responsible for the mistakes that occur – tell me how paying a full-time swe NGN70,000 is supposed to be a job.\nAnd just like a production line, some items in the line perform well at the current stage. For others, the machine might be overheating or overpowered, the materials might be of poor quality or exposed to moisture, or the belt might be contaminated. Hence, they fall off the line due to quality assurance and are reprocessed. It might take more time to replace the person you look up to, and that is completely fine!\nThis also doesn’t mean you are modelling your life against someone else’s. My dad doesn’t write code, my mentor doesn’t teach Arabic and Islam, and my other teachers don’t possess the same skills as these two - they have special knowledge or traits unique to them.\nI write code, and I don’t teach Arabic, I have lived in ways very different from what they had. I have no farming experience, had a way more challenging childhood than my Dad could narrate, and a far enjoyable career than my mentor, so far. But looking back, I could have felt, was this possible, at all?\nTo some, backgrounds, education, and networks are a disadvantage, and yes, how you start determines how you end, but that does not mean that every child from the gutters of Ajegunle can go on to rule the world, nope. This suggests that, despite the quirks that come with every human being, there is a forward path for our lives. And who knows, one of the forward paths could be a place of impact? Great things can happen from a disadvantaged place, aye?\nAnd again, going from a lowball experience directly to a more impactful one does not just happen. You have to be willing to give what you look forward to requires, and if not more, remember that the kind of life you aspire to is the prayer point of many people, including you!\nNo be only you like better thing!\nNo one has a complete disadvantage; your uniqueness positions you for what you should do, right now, and what you could do in the future. You could have an average face and a good voice, making you a suitable candidate for a voiceover artist job. You could hate the crowd, but it\u0026rsquo;s a good fit as a backup singer? You could be short with good stamina, a good athlete for a particular sport, but not basketball?\nFind a vacant position that fits you perfectly, even if you have no prior experience – this reduces extraneous requirements. Rinse and repeat until the end of your career, and possibly, your life.\nWhen considered deeply, no one is disadvantaged.\nDumbfounded by that fact. How were bank apps built and developed, airline software delivered on diskettes and USB flash drives? This was astonishing to me.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","permalink":"https://limistah.dev/essays/0010-follow-the-money/","summary":"There are too many things to be done from a small place. Like a production line, whose final product was a tiny piece of dirt material that became valuable after going through conveyor belts!","title":"Zero to One: Transitioning over the Conveyor Belt!"},{"content":"We’re Not Solving a Criminal Case 😉\nBack in 2021, while working at eConnectNP as a Software Engineer, I had the chance to work alongside a very good friend of mine, Cyril. We had many interesting conversations, mostly because we shared similar views on many things. One topic that stood out was how people with incredible talents often fail to live up to expectations—how they don’t end up living the victorious life you’d expect.\nThe first time we spoke about it, I said the problem was that they didn’t take their skills seriously enough to make a career out of them. The second time, I added that even when they did, they didn’t treat it as a business.\nIn my last post, I emphasized the importance of choosing what to work on. But here’s the thing: that’s not enough. I ended that post by talking about how first-class students often don’t translate their academic success into real-world success. This post is here to show you why simply having a bank of skills doesn\u0026rsquo;t guarantee you\u0026rsquo;ll get rewarded for them—and how people with average abilities sometimes do better in life than those with the most talent.\nLet me walk you through this thought process.\nWhy do you think we all tune in to watch the FIFA World Cup?\nA quick answer: You enjoy sports.\nBut have you thought about how it\u0026rsquo;s even possible for you to watch a live match happening thousands of miles away?\nYou might say satellite TV or live internet streaming.\nSure. But let’s go a bit further. What exactly makes it possible for you to sit comfortably at home and watch the game live?\nWell, you probably pay for a subscription or buy data to stream it.\nExactly. But why do you have to pay?\nBecause you’re being charged for using the service.\nTrue. But it’s also a charge for someone’s expertise.\nYou might hear people say footballers earn too much, and I’d agree. But the better question is: are we paying them to do what they’re incredibly good at?\nThink about musical artists. As a Nigerian, my favorites are Wizkid and Olamide—and with money in mind, I’d also add Don Jazzy to that list. No one argues their talent. Whether they trained for it or it’s natural, they’ve reached a level of mastery few can touch. And they’ve been rewarded accordingly.\nBut here’s the trillion-dollar question: how many equally talented and hardworking artists exist without even a fraction of their success? And we can go the other way: how many average artists exist and thrive more than talented ones?\nIf you dig deep enough, I often say you’ll probably find someone who plays football better than Messi and Ronaldo combined. Or a genius who could teach Einstein a thing or two about Physics. Or a golfer more skilled than Tiger Woods. The real issue is where these people apply themselves—and then how they apply themselves.\nThe level you start at often determines how far you can go. Few street footballers make it to the top leagues in Europe. But those who start in proper football academies usually have a better shot. The same goes for the NBA. Getting scouted off the street is much harder than rising through an organized system. That’s why some parents are very intentional about where their kids apply their talents.\nBut even after applying yourself, you still need to get rewarded. As my dad always says, \u0026ldquo;Knowledge or effort without reward is like a tree without fruit.\u0026rdquo;\nAnd if you want to enjoy the fruits, you need to define what “reward” means to you. Is it fame? Respect? Genuine love from people? All of these, one way or another, tie back to money.\nLiving is expensive! — Ej\nLet’s be real. You need money to live. And as a professional, you need money to keep doing what you do! If Olamide hadn’t earned from his early efforts in music, things could’ve turned out very differently for him. The biggest threat would’ve been distraction—the number one killer of dreams.\nDistractions aren’t always external. Back then, Olamide or Wizkid would’ve needed money for studio sessions, beats, video shoots, and promotions. If they didn’t earn anything from music, they’d have had to do something else to raise the money—time that could’ve gone into improving their craft. And as they got older, responsibilities would only increase—siblings, parents, their own families.\nNow, think about yourself.\nYou weren’t trained to write a new theorem or invent the next big thing. Even inventors are in it for the money! So, what profitable skill are you offering the world that can translate into income? What are you giving people that makes them believe you’re worth paying?\nWhen you imagine talented people, you probably assume they succeed from day one. But turning a raw skill into something people value is harder than solving a complex math problem or understanding physics laws. What makes people like Olamide, Wizkid, and Don Jazzy stand out is that they understand the concept of the value chain.\nEven as an independent artist, you can’t succeed alone. You might create your beat and master your track, but that’s not enough. Your music needs to pass through the hands of marketers, promoters, and connectors who can bring it to the ears of big audiences. That’s the value chain. If your music is good, people will hear it—somewhere, somehow.\nThe same applies in every industry—sports, medicine, tech, you name it. No one works in isolation. Everyone plays a role in a massive machine. Some people are the engine. Others are the spark plugs, the belts, and the wires. Without each part, the system doesn’t run.\nIn football, imagine watching a match without commentary. Boring, right? So, commentators are part of the value chain. Even the companies that make the footballs—Nike, Puma—are part of it. Everyone contributes to making a 90-minute match something worth paying to watch.\nYour skills will set you apart, but they won’t sustain you on their own. Many entrepreneurs think their product is enough to make them rich. It’s not. You still need the right people to connect the product to the right audience. Sometimes, you might have the audience but no product. It’s rare to have both.\nThat’s why the value chain is essential. It helps you determine where your skill fits best in the broader context. Imagine Peter Drury as a defender, or Wizkid as a backup drummer, or Eminem as a promoter. That would be a waste. Put people in the right place, and they become value creators. When money flows through the system, it flows through them.\nWhen you\u0026rsquo;re chasing the money in an industry, ask yourself, What\u0026rsquo;s the total value of this industry? For example, in 2013, it was reported that Lagosians spent over ₦36 billion on owambe annually —that’s entertainment alone! That’s an eye-opener. Next, ask: What makes up this industry? And finally: Where is your unique advantage?\nTech is a huge space. Think about Apple, Microsoft, and Amazon. If you\u0026rsquo;re a guy from Ajegunle with youth and brains, where can you apply yourself? Software engineering? Is that enough? Maybe. 😏\nAdvertising is another powerful industry. Companies pay big money to people with platforms to promote their products. Every year, there’s a massive budget set aside for ads. TV and radio stations get it. Billboard owners get it. And now, influencers get it. Sabinus once said he could charge ₦1 million per post. Kim Kardashian charges $1 million!\nIn sports, you see it. Footballers have a transparent salary structure. You’re paying them to stay fit and play for 90 minutes. That fee also pays for Drury’s mortgage, the ball boy on the pitch, the stadium lights, and the doctors on standby.\nAnd this applies to whatever you pay money for; your transport fare is not just paying the agbero, it pays for the fuel they buy as well. Your phone doesn’t just pay Apple; it pays the guys at the computer village bringing it closer to you. The snacks you eat doesn’t just make Dangote richer; they pay for the truck driver’s children’s school fees too!\nEven scientists like Neil deGrasse Tyson have YouTube channels. He talks science. You watch. YouTube pays him. Footballers play. You watch. They get paid. You write code. Someone uses the code, your company makes money, and your company pays you.\nSo here’s the truth:\nOnce you become part of a system that solves a real problem, for an audience that’s big enough, you will get paid enough.\n","permalink":"https://limistah.dev/essays/0009-follow-the-money/","summary":"Why Skill Alone Isn’t Enough—Understanding Value Chains, Strategic Positioning, and the Real Reason Talent Gets Paid","title":"Follow The Money"},{"content":"Motivation Experiencing binary search was like a divine revelation of the ingenuity of algorithms. How can computer code be so clever and efficient at the same time? We don\u0026rsquo;t thank the inventors enough 😏.\nAmong the great, simple, and everyday algorithms used is merge sort. It is a divide-and-conquer algorithm that works by taking a big problem, chunking it down into the smallest possible units, solving them, and adding up the results together. Read more about it here.\nWhile deeply studying merge sort, a question I further asked after noticing its patterns is: Is this under the hood another binary search algorithm?\nThe beautiful binary search Searching is fundamental to human existence, we are always asking the what, why, where, who, and when questions. This has resulted in most of our inventions, computers being one of their greatest. So, given an array of five items [4,5,1,2,4], how can you get an item from the list?\nAn easy approach would be to go through each item, compare them to 1, and return to the position where it was found. This approach is called linear search, which has been implemented here. This raises a question: what happens when the list is large enough, for example, your WhatsApp contact list? Searching through these items in linear time can be fast for 10 10-item lists; when the list becomes sufficiently large, the time it takes will frustrate any human.\nWhat if the list is sorted ([1,2,3,4,5]), and we need to get the item at position 4 from the list? We have to change our thinking model. Going through each of these items linearly is lavishing time! Which introduces the binary search algorithm.\nWe can split the items into two arrays, [1,2] and [3,4,5], determine where the item we are searching for can be(left or right), discard the left array, and divide the right array into two [3], [4,5]. We discard the left array again and divide the right array into two [4] and [5]; the target is the only item in the right array. Notice it took us three divisions, two extra variables to store the separate arrays, and two decision statements - Is the item on the left or the right? Is the item the only item in the array?\n1 2 3 4 5 6 7 8 9 10 11 12 def hypothetical_search(arr, target): while len(arr) \u0026gt; 1: mid = len(arr) // 2 left = arr[:mid] # Split into left half right = arr[mid:] # Split into right half if target in right: arr = right # Keep right half else: arr = left # Keep left half print(f\u0026#34;Final array: {arr}\u0026#34;) return arr[0] if arr and arr[0] == target else None We can use the above procedure to find out if the item is on the list. What if we need the index where the item is?\nRecall our sorted array of 5 items [1,2,3,4,5]. Instead of dividing, we can have two variables: start point and end point. The start point of the array is 0, the endpoint is the length of the array -1 - in our case 4, and finally, keep a midpoint (floor division of the length of the array).\nThen, we can check if the target is equal to the value of the midpoint. If yes, we return the index of the midpoint; if no, we check if the item is greater than the midpoint, if it is, then we set the value of the left handle to be the midpoint, if it is not, we set the value of the right handle to be the midpoint and this is to discard half of the items in the list, either from the left till the midpoint or from the midpoint to the right.\nWe do these steps up until we find a midpoint where the value is the same as the target.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 def binary_search(A: list, n: int, target: int): l = 0 r = n m = n//2 # floor division while l \u0026lt;= r: m = l + (r - l) // 2 if (A[m] == target): return m if (A[m] \u0026gt; target): r = m - 1 if (A[m] \u0026lt; target): l = m - 1 return -1 For the most binary search, it could be written with a loop, but it is cleaner to implement it as a recursive algorithm - this is proving the statement that nearly all recursive algorithms can be implemented with a loop.\n1 2 3 4 5 6 7 8 9 10 11 12 13 def binary_search_recursive(A: list, l: int, r: int, target: int): if (l \u0026gt; r): return -1 m = l + r // 2 if (A[m] == target): return m if (A[m] \u0026gt; target): return binary_search_recursive(A, l, m - 1, target) if (A[m] \u0026lt; target): return binary_search_recursive(A, m - 1, r, target) def binary_search(A: list, n: int, target: int): return binary_search_recursive(A, 0, n, target) The iterative implementation explicitly expresses the formal definition and internal operation of a binary search algorithm,\nIs the midpoint element the target?\nYes, return it.\nNo, then,\nAdjust the left or right handles and check the midpoint again.\nSweet!\nMerge Sorting Faced with the challenge of sorting the list [4,5,1,2,4] in ascending order, as we did for searching, we could pick one item at a time and insert it into its exact position. We could achieve this using the insertion sort algorithm:\n1 2 3 4 5 6 7 8 9 10 11 12 def insertion_sort(A: list): # sort elements from the second item in the list, we assume the first item is already sorted. for i in range(1,len(n)): key = A[i] j = i - 1 while j \u0026gt; 0 and A[j] \u0026gt; key: A[j+1] = A[j] j = j - 1 A[j+1] = key return A print(insertion_sort([4,5,3,2,1])) # -\u0026gt; [1, 2, 3, 4, 5] How this works has been discussed here. Insertion sort runs in O(n^2), but with a divide and conquer algorithm, we can run in O(nlgn) time.\nMerge sort is another divide and conquer algorithm that works just like any other would. It takes an input array, breaks it into chunks of smaller subarrays, sorts the subarrays, and merges the results, all these in O(nlgn) time.\nSorting the list of numbers, instead of taking an item and inserting it in place of another, we rather tear apart the entire list and rebuild a new one with the right order. Starting with [4,5,3,2,1], we can split it into [4,5] and [3,2,1]. And again, we can split these into [4] and [5] for the left, and also [3] and [2, 1] for the right. Lastly [2] and [1]. So far, the breakdown looks like this:\n1 2 3 4 [4,5,3,2,1] [4,5] [3,2,1] [4] [5] [3] [2, 1] [2] [1] We begin from the last item row, sort the items, and then merge them in place and up to the top row:\n1 2 3 4 5 6 7 8 [5,4,3,2,1] [5, 4] [3,2,1] [5] [4] [3] [2, 1] [2] [1] =================================== [4,5] [3] [1, 2] [4,5] [1,2,3] [1,2,3,4,5] In implementation, the code looks like this:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 def merge_sort(arr): if len(arr) \u0026lt;= 1: return arr mid = len(arr) // 2 left = merge_sort(arr[:mid]) # sort the left items right = merge_sort(arr[mid:]) # sort the right items return merge(left, right) # merge the results def merge(left, right): sorted_array = [] i = j = 0 while i \u0026lt; len(left) and j \u0026lt; len(right): if left[i] \u0026lt; right[j]: sorted_array.append(left[i]) i += 1 else: sorted_array.append(right[j]) j += 1 sorted_array.extend(left[i:]) sorted_array.extend(right[j:]) return sorted_array A formal analysis of divide and conquer can be found here. This works by splitting the array into two until there is only one item remaining in the final array. Then it walks back up, bringing the items in the left and right arrays to compare their first items to identify which comes first to insert it into the position in the main array.\nWhen one of the sub-arrays is empty - nothing left to sort - it spreads the remnant in the nonempty subarray to the right of the main array. Hence, the left items are always sorted after each merge procedure call.\nThe meat of merge sort is in the merge procedure, but regardless of the subtleties, the importance of the merge_sort procedure can not be ignored. A closer look reveals a resemblance to its counterpart divide and conquer algorithms, in our case, the binary search!\nhttps://people.eecs.berkeley.edu/~vazirani/algorithms/chap2.pdf Like Binary Search Like Merge Sort The question I often get with merge sort and other divide-and-conquer algorithms is: How does this work?\nThe problem is not understanding merge sort, it is in understanding divide and conquer algorithms.\nThe goal of a divide-and-conquer algorithm is to break a problem into the smallest unit of subproblems that can be solved easily. Not just for sorting or using binary search to find an element\u0026rsquo;s index in a list. We can use it for other problems like the below:\nEPI Page 1 When conceptualizing a problem, the first insight is understanding if the solution to the problem can be determined in a single execution statement and if the problem input can be split into smaller versions such that a single split is a subset of the actual input such that we could solve it easily.\nIn the above example, although two values are required - entry and exit values- note that the lowest buy price will always be on the left, and the highest sell price will be on the right. If you have the array [[10, 12], [12, 11.5], [11.5, 13],[13, 9.01]], when you buy at 10 from the first item in the list and sell at 13.00 in the last index. A brute-force solution should run in O(n^2).\nHowever, the best solution will continuously split the array into halves(the divide stage), up until there is one array left, and track the optimum buy price from the left array and the optimum sell price in the right array. This takes into consideration the intricacies of empty an array, an array with one element, and an extra edge case unique to this problem, when the price decreases monotonically - then there is no way to determine a selling price.\nNotice that I have referenced a problem that uses divide and conquer to solve a problem that seems different from merge sort and binary search; it is expecting us to pick(which might sound like a search problem) two best items from a list.\nWhat you should know When these questions arise, it is as a result of an incomplete understanding of fundamental concepts.\nWhile our jobs might require us to sort a list or search for an item in a sorted list – knowing merge sort will suffice – real-life problems do not present themselves in this fashion, as demonstrated.\nWhen solving a problem, the default is to look for a brute-force solution that performs in the worst possible time but satisfies the definition of an algorithm. To get the best possible performance out of the algorithm, the saving grace is understanding the fundamental concepts and being able to relate a problem to one of them.\nSo, always choose to understand the fundamentals rather than example use cases of a fundamental concept. After that, merge sort, binary search, and other seemingly hard algorithms will present themselves, like the sun in the day!\nShialom\n","permalink":"https://limistah.dev/posts/merge-sort-binary-search/","summary":"As a divide and conquer algorithm, in this article, we look deeply into merge sort, simplifying its core concepts with a binary search algorithm.","title":"Merge-Sort like a Binary-Search"},{"content":"Now that you know how to manage your time, you must work hard and understand that there are no shortcuts. You can get farther in life with just this, but I would advise you to stick around for more cappings. Yes, more… 😁\nIn Outliers, Malcolm Gladwell has a section called Problem with Geniuses. It is a universal version of the typical Nigerian notion that first-class graduates earn peanuts. It is not that they don’t understand the subject matter; we know there are not enough jobs, but there are enough for first-class graduates, so what could be the problem?\nTrust me, it is not the government or the politician; it is a result of their decisions.\nYour browser does not support the audio element. Earlier this year (2024), people shared how being a first class has helped them be ahead and remain ahead of their peers.\nOne of them that struck me the most is the man who shared he had to write ICAN during NYSC, rejected a job offer while still jobless to later work at KPMG Nigeria the following year, and now he is a manager in the UK.\nWe could argue that he is not a genius; following his story, some characteristics differentiate him from most first-class graduates, which I believe are the things he understands about deciding what to work on.\nFor generations, it has been known that success in life is determined majorly by three choices:\nWho you choose to marry Where you choose to live What you decide to work on. All of these revolve around the choices we make that ultimately result in a certain outcome. Think of it like mathematics (pun intended as an engineering graduate) 1+1=2, 3-1=2, -1+3=2, or even as a computer (as a programmer), it is always garbage in, garbage out!\nIf I would apply this to some things about me:\nI have a blog at limistah.dev, if you are not one of my followers, you would have had no idea about it until now. However, I have an article on Smashing Magazine and another on LogRocket; these would likely catch your attention better.\nAlso, which sounds better about me: I have open-source projects under a parent project called ObjectSpread and I am the founder and lead maintainer, or I have contributed to popular open-source projects like Vault by Hashicorp, PayloadCMS, Gin-Swagger, etc.\nI believe Smashing Magazine caught your attention better; same with Hashicorp, Payloadcms, and Gin. With these, you can attest to my expertise. Yes, my blog says enough; a more reputable source removes all the unnecessary doubt.\nIt is the same with the kind of school you attend, which determines the kind of education you get. No one should be labeled by the kind of school they attended; still, there is a minimum for this rule to apply.\nIt is the same with the kind of neighborhood you grew up in; this should label no one, but stereotyping exists as the sad truth.\nWhile it is great to work hard, even tough, the platform and problem you eventually choose to work on also matter.\nWhen I got my first full-time role, my colleagues at the office were discussing how career progression should go, to them, they believed their careers had started. I said my career would start until I worked at Paystack or Flutterwave, which did not happen; I would work at Oasis Living in Nigeria months later.\nIn 2022, my brother got admitted to OAU and was waiting for resumption. I told him, \u0026ldquo;Hey, you could use your JAMB result to get admitted to Covenant, you know?” He resisted and argued the school fees were expensive (you can’t blame him).\nI told him, “Well, it is a fixed cost, and you get to graduate in 4 years from Covenant University.” He resisted, still.\nThis year, we were randomly discussing, and he said he regrets not accepting my offer for him to attend Covenant University. It has been 3 years since then, and he is just resuming part 2 of his program, but with Covenant, he could be resuming part 4.\nThat is a great lesson for him on choosing wisely regardless of the cost. For him, that was not the beginning.\nThere is a new revelation that I do bring to his attention. Immediately after high school, the only admission he could secure was to a polytechnic, which I advised against. It was tough for him back then, but it is a good decision for him now.\nI reminded him how many years had passed and how much money he had spent. And had he accepted the offer, he could have a diploma.\nIn contrast, another person at a university spending the same amount of time, with a slight difference in cost, would get a degree; he always nods his head in agreement—my consolation to him on missing the Covenant offer.\nIt is glaring; we all have 24 hours in a day, but with different opportunities, agreed. Most times, what differentiates us from how we end up in life is the kind of problems we choose to solve and how we choose to solve them.\nI believe working for just working sake is fine, but for everything we do in life, there is a result and an end goal. Life is a journey with many stops; some stops could put you on another bus based on how you did on a previous bus. And we could agree that school is a stop too.\nChoosing to work doesn’t have to be doing just work. It has to be impactful work. Most times, we don’t have the requirement to be at a place of impact, which is the reason why it is best to outwork everyone at the current stage to create an unfair advantage for the next stage—working extremely harder now, but on the right problem.\nGoing back to the story of our accountant: He chose to work at places of impact, but he understood the requirements ahead, which helped him prepare what he needed to get to places of impact, and those impacts propelled his growth—it was not magic.\nThe fascinating facts from these choices are:\nYou will marry someone (sorry to the woke marriage is a bondage ideologist) You will live somewhere You will work on something (even if you decide to be an entrepreneur) Then why do you want to:\nJust marry anyone? Just live anywhere? Work anywhere? Why can’t you:\nMarry the best person for you? Live at the best place for you? Work at the best place for you? I have always been asked if I have an idea around AI, and my response is always: I don’t want to build with AI—building with AI is me trying to create another AI platform.\nI want to build something for the platform, as Andrew Ng always advises, by applying AI to real-world problems.\nThis also translates to adjacent growth; if AI grows, my stuff grows too. This can also be seen as investing in stock. Why would I want to bet my all on new stuff when I can leverage a part of bigger stuff and grow with it from there?\nYou can stumble on impactful work and life; that is not my genuine advice for you, as it results from low self-awareness. You don’t want to control everything, but be sure enough to know what the outcome of what you are doing could lead you to, then let life happen in between.\nIt is like trying to create your own Google rather than working at Google or with Google. By creating yours, the chances of succeeding are slimmer, and the chances of impact are almost none.\nThere are many lives to be impacted by working at Google or doing something with Google. Google gives you a readily available platform that you would spend years building; the case of ChatGPT going from zero to a million users in 24 hours is tempting.\nAs always, be real with yourself; can you create such an impact? If you can, what exactly are you trying to do differently? ChatGPT is a novel solution that gives an interface to decades-long work in AI/ML.\nStill, I am not asking you to ditch lowball opportunities. I am simply asking you always to define impact in your work. Cal Newport mentioned that when he was choosing the school to join after his PhD at MIT, he decided a small university where there was a lot of work to be done and he could be a pioneer of the department.\nHe chose Georgetown University over other established universities where there was still work to be done, but his work would become another document in the library, hard to find, and he became another faculty member rather than a pioneer.\nYou could choose to work in a new, emerging field, such as an up-and-coming company; please do this in an environment where there is a potential platform for the field to grow. Slack was a company for gamers that could have been hard to find in the gaming industry, but it finally became famous as the leading workplace solution.\nNo hard/fast rules; just being aware is the ultimate. The problem with first-class graduates in Nigeria is that they do not translate the style of work they used to achieve their first class in school to their work life.\nSome stumbled upon a first class, which I would even debate as a lack of self-awareness; you don’t work that dedicatedly hard and not expect a result. For some, it was deliberate, preplanned, and a goal they sought out when setting foot on their campuses. Why not do the same for work life?\nJust as graduating with a first-class, knowing what is possible and choosing to do the most impactful work is the way to go further and stay ahead, doing hard work won\u0026rsquo;t—especially on the wrong thing.\n","permalink":"https://limistah.dev/essays/0008-if-you-retire-you-expire/","summary":"Reflection on choices, intentionality, and working on impactful problems.","title":"Work harder, but not just harder"},{"content":"A good friend of mine, Kehinde, shared a brilliant piece with me on working hard and taking everything really seriously. It is an insightful read and deeply investigates work-life balance, how hard a person should work, and what they should focus on, life or work.\nFollowing up on moving fast but smoothly, and Well, you wouldn\u0026rsquo;t be missed; both of these discussed more about being a hard but smart worker. The next question is: What about my life?\nMy best takeaway from the article is the closure:\nIt isn’t a revolutionary idea that people who are excellent in their fields often get there by trying really hard. If you can figure out the difference between busy-work that only benefits your employer, and the kind of work that makes you as a person feel like you’re making progress and becoming more skilled, then you’re ready to learn.\nWhat about your life? Really?\nLet’s try the biographies of a few notable people:\nAlan Turin\u0026rsquo;s contributions to computer science are well-known.\nIsaac Newton is renowned for his contributions to science in general, calculus, and gravity.\nIt is general relativity that makes Albert Einstein famous.\nFootball is how Abedi Pele is best remembered.\nAs far as football players go, Messi is the greatest of all time.\nTiger Woods is well-known for his accomplishments in golf.\nWilliam Shakespeare is known as the greatest playwriting and poetry.\nThomas Edison is known for his work on the light bulb.\nFredrick Lugard is known for the amalgamation of Nigeria.\nOne notable mention is Pharoah, known for his tyranny during his rulership. But which of the Pharaohs? Cleopatra? No!\nAll the names mentioned have etched their names on history and have been allowed to work on things that affect many lives directly.\nYour local teacher would be remembered too, not for his family, not for his jovial attitude, but for their impact on their students.\nAleem is a software engineer; that is all I can tell about myself. What would you say about me when I am not here and a recommendation is to be made on my behalf?\nWould you think of me as that competent engineer? Would you want to be associated with me and my work?\nI think regardless of how we paint work-life balance, we forget what makes our life balance is the impact we create on others, which gives us rewards that we use to keep other parts of our lives alive.\nAs said in the referenced article, our daily jobs do not give us the leverage to have as much impact.\nHow I measure a job is how fast I would outgrow my responsibilities—I know I would, but most times the job stops becoming interesting after a few months.\nInteresting for me is when I have problems and no solution has occurred to me immediately.\nMost jobs are like that. Think of a secretary, a plumber, a software engineer, or an electrician. Our jobs are often repetitive and could become second nature, like a habit.\nSo, what happens when you need to find a new job? Do you think every other company has the same kind of problem/tools/process/culture as yours?\nI don’t think so. I am sure you would agree.\nIn this modern artificial intelligence age, the need for adaptability has never been so in demand.\nAs a professional, how fast can you adapt to a new environment, or how fast can you adapt when the conditions of your current environment change?\nAdaptation comes from experience; in the case of evolution, once a habitat is altered, only a variant of species that have tolerance to the changes survive, and the others die.\nMy question is, do you want your professional life to go extinct because something changed?\nI read Who Moved My Cheese countless times; it is part of the books I read frequently to remind myself—nah, you can’t rest yet!\nHad Sniff and Scurry rested after finding their first stockpile, they could have faced the same problems Haw and Hew faced. Before the cheese finished at section C, they found another.\nThey devised a strategy to spend some time after eating to find other sources. Their strategy is so interesting to me that once applied to a work-life could help grow some adaptation skills.\nYou could spend a few hours weekly searching for trends and experimenting on them; it doesn’t have to be the work directly assigned to you by your employer. You are trying to adapt to a future change in your professional ecosystem because you don’t want to go extinct.\nRelentless, Tim Grover\nThe day your professional death starts is when you stop showing up, giving your best, following the trends, and joining the relevant ones.\nYou don’t have to be the greatest like the ancestors and their predecessors; not everyone can even be famous in their local community. But that doesn\u0026rsquo;t mean you should not do your best to hold a reputation for which your friends respect you.\nIf you wish to adapt, your other life would suffer a hit; no sugarcoating. Is there a balance, really? For some people, yes; for most, no.\nYou only want to ensure you are there for the important and necessary events/occasions that you are required for your people—your family, parents, siblings, friends; and yourself—health checks, gym, food, keeping clean. Other times might be spent staying adaptable.\nIn the Suits series, Alex Williams asks Robert Zane why he stopped taking up cases after retiring. He gave the narration of a fund manager, who, when active, would be called many times a day by investors and borrowers, and upon retiring, the calls stopped coming.\nInvestors and lenders call the new manager that can solve their problems.\nRobert mentioned a statement that sticks with me:\nThe day you retire is the day you expire.\n","permalink":"https://limistah.dev/essays/0007-if-you-retire-you-expire/","summary":"The fine line between having a life and creating a meaningful impact","title":"If you retire, you expire."},{"content":"Time is a linear monotonically increasing value and to keep track of it, a system has to be powered to take note of every tick.\nFor a microcomputer running an operating system that can go on and off, this means there is a need to constantly update time whenever the computer comes on. That is not the case because of some intelligent mechanisms that computer hardware and operating systems use to keep track of the current time.\nReal-Time Clock A Real-Time Clock (RTC) is an electronic hardware component that helps track the current time and date, even when the system is powered off.\nHow it works RTC uses an oscillator that generates a stable and precise frequency, typically 32.768 kHz.\nThe pulses from the oscillator are sent to counters within the RTC chip where seconds, minutes, hours, days, and so on are measured. These counters increment to keep track of the current time and date.\nThe values for the current date and time are stored in registers contained within the RTC chipset, these registers also help to keep track of configuration values such as alarms, enabling and disabling the clock, and adjusting for leap years.\nRTC chips can be powered by a CMOS RAM battery but are mostly powered internally by an alternate power source, usually a replaceable lithium battery or modern supercapacitors.\nNetwork Time Protocol Although RTC can keep track of time using oscillators, there is no source of truth for what the current time might be.\nNTP is a protocol designed to synchronize the clocks of computers over a network. It aims to keep the system time in sync with more accurate time sources, such as atomic clocks or GPS clocks.\nIt implements the intersection algorithm and uses a client-server model to send time stamps which is accurate to 10 milliseconds over the public internet.\nRTC and NTP As mentioned earlier, time is monotonically increasing, operating systems use a combination of RTP and NTP to maintain a consistent time that users see.\nOn Windows, support for NTP is inbuilt can be configured through the system settings or the command line, and is managed by Windows Time service. This service can be manually started with:\n1 net start w32time On Linux, support for NTP is also built-in, but can be installed manually:\n1 2 3 sudo apt-get install ntp # Debian/Ubuntu sudo yum install ntp # CentOS/RHEL sudo dnf install ntp # Fedora And can be started as a daemon afterwards:\n1 2 sudo systemctl enable ntp sudo systemctl start ntp RTC on mobile devices For devices that do not have any networking support but can send and receive radio signals, there is a Radio-based RTC that mobile network providers use to send current time to mobile devices.\nThis can explain why time is always correct on mobile devices even in remote areas without access to the internet.\nWall Clock and Monotonic Clock Computers rely on interrupts and signals to function properly like scheduling processes. Interrupts are implemented with timers that send signals at specific intervals.\nWith the time information provided by RTC, if the time is updated by NTP to an older or future time, this causes problems for the operating system to determine what processes to interrupt or resume.\nTo solve this problem, computers use a monotonically increasing counter called the Monotonic Clock. It provides a continuously increasing value that represents the total elapsed time since an unspecified starting point (often the boot time of the system).\nThis introduces a second clock to the operating system, and brought about the name Monotonic Clock, while RTC is referred to as the Wall Clock.\nMonotonic clocks are useful for:\naccurately measuring the elapsed time between events implementing timeouts and delays in applications where time needs to be measured consistently and without interruption Ensuring reliable timing in distributed systems where synchronized actions are crucial. Most programming languages provide access to both time:\nPython 1 2 3 import time print(\u0026#34;Current time:\u0026#34;, time.time()) print(\u0026#34;Monotonic time:\u0026#34;, time.monotonic()) Ruby 1 2 3 4 current_time = Time.now puts \u0026#34;Current time: #{current_time}\u0026#34; monotonic_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) puts \u0026#34;Monotonic time: #{monotonic_time}\u0026#34; JavaScript 1 2 3 4 console.log(\u0026#34;Current time: \u0026#34;, Date.now()) const monotonicTime = performance.now(); // milliseconds since page load: console.log(`Monotonic time ${monotonicTime}`); Go 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package main import ( \u0026#34;fmt\u0026#34; \u0026#34;time\u0026#34; ) func main() { fmt.Println(\u0026#34;Current time: \u0026#34;, time.Now()) // Get the current monotonic time monotonicTime := time.Now().UnixNano() // nanoseconds since epoch fmt.Printf(\u0026#34;Monotonic time: %d\\n\u0026#34;, monotonicTime) } https://go.dev/play/p/aPgf5HBzSG6\nC 1 2 3 4 5 6 7 8 9 10 11 12 13 #include \u0026lt;time.h\u0026gt; #include \u0026lt;stdio.h\u0026gt; int main() { struct timespec ts; clock_gettime(CLOCK_REALTIME, \u0026amp;ts); printf(\u0026#34;Current time: %ld.%ld\\n\u0026#34;, ts.tv_sec, ts.tv_nsec); struct timespec mts; clock_gettime(CLOCK_MONOTONIC, \u0026amp;mts); printf(\u0026#34;Monotonic time: %ld.%ld\\n\u0026#34;, mts.tv_sec, mts.tv_nsec); return 0; } View on Replit\nConclusion This post shared the concept of time in computer system architectures and drilled down to discuss more on the different types of time available within an operating system and examples of how they can be retrieved in different programming languages.\nau revoir!\n","permalink":"https://limistah.dev/posts/time-in-computer-systems/","summary":"Learn how time tracking is implemented from the hardware.","title":"Time in Computer Systems"},{"content":"Brief Programming languages have the notion of constants which means \u0026ldquo;variables that can not be mutated once declared and initialized\u0026rdquo;.\nGo also has almost the same meaning, but in a different context. To initialize a variable as a constant with a value of 10, we can do something like this:\n1 const DISCOUNT = 10 In Go, constants mean \u0026ldquo;storing a literal to a variable\u0026rdquo;, this can be seen as a version of pattern matching in Erlang.\nLiterals in go are constructs that can create an instance of strings, numbers, booleans, composite structures, functions, or expressions. Hence unlike many programming languages, constants in go can be const FN = fun () { fmt.Println(\u0026quot;Hello World\u0026quot;) }.\nAn explanation Initializing a variable as a constant means the variable holds the value and possibly the type of the literal it is assigned to. Anywhere the DISCOUNT above is used, the compiler assumes that as writing the integer literal 10.\nAt compile time, the compiler swaps all representations of a constant value with the value it was assigned to. Such that\n1 2 3 func main () { fmt.Println(\u0026#34;%v\u0026#34;, DISCOUNT) } would become\n1 2 3 func main () { fmt.Println(\u0026#34;%v\u0026#34;, 10) } Big difference In some programming languages, Constants mean the value of a variable once declared and initialized can never change\nIn contrast to what we have understood – constants are used to enforce that a variable is immutable, in Go, constants can be used to assign names to a literal exactly once, while at compile time, the name is swapped with the value of the constant - ensuring cleaner code, the idiomatic go.\nShalom\u0026hellip;\n","permalink":"https://limistah.dev/posts/go-const/","summary":"\u003ch2 id=\"brief\"\u003eBrief\u003c/h2\u003e\n\u003cp\u003eProgramming languages have the notion of constants which means \u0026ldquo;variables that can not be mutated once declared and initialized\u0026rdquo;.\u003c/p\u003e\n\u003cp\u003eGo also has almost the same meaning, but in a different context. To initialize a variable as a constant with a value of 10, we can do something like this:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-0-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-1\"\u003e1\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-go\" data-lang=\"go\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003eDISCOUNT\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e10\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003eIn Go, constants mean \u0026ldquo;storing a literal to a variable\u0026rdquo;, this can be seen as a version of pattern matching in Erlang.\u003c/p\u003e","title":"Go - constant variables and const keyword"},{"content":"Brief Although Go releases are incremental and follow the Go compatibility promise: unless the change is required for a bug or security fix, the version starting with 1 won\u0026rsquo;t experience any backward-breaking change to the language or standard library, you can have a reason to use an old version of the language.\nIs there a clean way to do this?\nRecommendation I recommend a tool like go version manager that helps to manage a systemwide version of the language. The advantage of this is the ease it allows. A simple gvm use 1.22, would ensure the 1.2 version of the langugage is installed system-wide.\nOptimal Solution A better approach is not to compromise the global Go installation but to run a specific code with a specific version of Go installation and discard the installation once done. Go supports this easily.\nTo do this, firstly, use the go get command to download a specific version of the language as a package:\n1 go get golang.org/dl/go1.22.3 The above command would download an executable named after the version. Now, use the executable to issue a final command that would do the setup.\n1 go1.22.3 download Once this is done, use the named executable(go1.22.3) to run go commands, example:\n1 go1.22.3 run main.go Cleanup Cleaning up the installation is the same as deleting the installation from the filesystem. The command go1.22.3 env GOROOT reveals the installation folder for the version of the go executable, now use rm command to delete it.\n1 rm -rf $(go1.22.3 env GOROOT) Finally, the system wide go installation, go1.22.3 is a package that has an executable, remove the installed executable with the command:\n1 rm $(go env GOPATH)/bin/go1.22.3 Obrigado\n","permalink":"https://limistah.dev/posts/go-run-with-a-specific-version/","summary":"This post shares how you can run an old version of Go while still having your machine\u0026rsquo;s default Go - which is newer.","title":"Running Go programs with a specific version"},{"content":"Motivation Pod scheduling can be a nightmare in a large Kubernetes deployment with many nodes having different configurations.\nConsider the configuration below:\nNODE A: 16vCPU, 10TB SSD Disk Space, 64GB RAM. NODE B: 4vCPU, 100GB HDD Disk Space, 8GB RAM NODE C: 8vCPU, 100GB HDD Disk Space, 8GB RAM, with additional GPU configuration And scheduling a new deployment to the cluster:\n1 kubectl create deployment --image=nginx --replicas=3 Kubernetes would schedule the Pods on a random node. Which is fine for general use cases.\nImagine if we are to deploy an LLM on NODE C, there should be a way to instruct Kubernetes to do just that.\nAlso, there should be a way to tell Kubernetes to not deploy anything to NODE C unless it was specified.\nIf you inspect the master node with: kubectl get nodes controlplane | grep -i taints this would return all the taints set for the master node that prevents the scheduling of Pods on the master node.\nIn Kubernetes, there should be worker nodes that Pod can run on and master node(s) that schedule the Pods to run on what node.\nWe can apply node affinity and node taint and toleration to control pod scheduling in Kubernetes.\nNode Taint \u0026amp; Pod Toleration Node Taint is like creating a condition that a Pod must adapt to for them to run on a node, or asking a node if they can accept a Pod.\nHere, we need to taint NODE A such that no other Pod should run on it except they have the right toleration to make the Pod adapt to the condition.\nTo taint a node, use the kubectl taint command:\n1 kubectl taint nodes nodeA size=large:NoSchedule The command takes a node name and the taint to attach to it in the form key=value:TaintEffect. There are three different TaintEffects:\nNoSchedule: Don\u0026rsquo;t schedule any pod on this node if they don\u0026rsquo;t tolerate this taint. Existing pods on the node that do not tolerate the taint will continue to run. PreferNoSchedule: Only schedule a Pod on the node if there is no suitable node for the Pod. NoExecute: Don\u0026rsquo;t schedule any pod that doesn\u0026rsquo;t have the toleration, also, evict any pod that is currently running but doesn\u0026rsquo;t have the taint. For a Pod to run on nodeA, it would have to tolerate the taint.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 apiVersion: v1 kind: Pod metadata: name: nginx labels: env: test spec: containers: - name: nginx image: nginx imagePullPolicy: IfNotPresent tolerations: - key: \u0026#34;size\u0026#34; value: \u0026#34;large\u0026#34; operator: \u0026#34;Equals\u0026#34; effect: \u0026#34;NoSchedule\u0026#34; The Pod manifest file has a tolerations section where key value and effect are defined, these must match the specification of the taint. The operator can either be Exists which means the key exists as a taint on a node - a value is not required for this, or Equals meaning the key, value, and effect must match a taint.\nScheduling this Pod would ensure it runs on NodeA.\nIf we have a similar Pod that should be scheduled on NodeA, but for some reason (not enough resources) was not, the Pods can end up on NodeB, or NodeC, which is not what is desired.\nAffinity Rules Node Affinity is a concept that helps Pod to choose where they can run on instead of the node asking if they can run on it.\nFor node affinity to work, a node must have a set of labels that Pods can request, in the case of our NodeC, we can set that using:\n1 kubectl label nodes nodeC purpose=gpu-compute With this set, Pods can look for the nodes that match a particular set of labels through a nodeSelectors field:\n1 2 3 4 5 6 7 8 9 10 11 12 13 apiVersion: v1 kind: Pod metadata: name: nginx labels: env: test spec: containers: - name: nginx image: nginx imagePullPolicy: IfNotPresent nodeSelector: purpose: gpu-compute This ensures that the nginx pod runs only on the Node that has a label called gpu-compute.\nWe could also use the nodeName to select a particular node if we are sure we have the information on the node\u0026rsquo;s name. But this is not idiomatic enough, nodename can change or the node can go up or down. Using nodeSelector is safer.\nUsing nodeSelector is restrictive, what happens if no node matches the label selector?\nNode Affinity expands on the idea of nodeSelector to have a robust configuration for the selection process.\nThere are three types of nodeAffinity:\nrequiredDuringSchedulingIgnoredDuringExecution: This configuration ensures that the affinity must match before a Pod can be scheduled to run on a node, but don\u0026rsquo;t eject any Pod already running on the node without a matching label. preferredDuringSchedulingIgnoredDuringExecution: This configuration checks if there is a node matching the affinity, if none, do random scheduling to any node, but don\u0026rsquo;t eject any Pod already running on the node without a matching label. requiredDuringSchedulingRequiredDuringExecution: This configuration ensures that a Pod affinity matches a node, and for currently running nodes, eject them if their affinity does not match. A Pod definition with nodeAffinity configured look like this:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 apiVersion: v1 kind: Pod metadata: name: nginx labels: env: test spec: containers: - name: nginx image: nginx imagePullPolicy: IfNotPresent affinity: nodeAffinity: preferredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: purpose operator: In values: - gpu-compute The In operator ensures that the values in the Pod definition file must match one of the values of node labels for the same key. Like doing an OR operator. Read more on the operators here.\nThis is not the end.\nTainting+Affinity=Effective Pod Scheduling Segregation With Pod affinity, we can end up having other pods not requiring GPU running on NODE C, this means having too much compute power than they require.\nThe problem with node tainting is the fact that nodes not satisfying the taint can also end up at nodes where they are not supposed to run. For example, a GPU-intensive Pod might get scheduled on NODE A, this is not what we want.\nThe fact is that both Pod Affinity and Node Tainting solve each other\u0026rsquo;s problems.\nFinal solution: Use both Node Taint and Pod Affinity to achieve a more effective Pod Scheduling segregation.\nFirst, attach labels to the nodes that require special attention:\n1 kubectl taint nodes nodeC purpose=gpu-compute:NoSchedule Second, attach taints to these nodes so they repel other Pods:\n1 kubectl label nodes nodeA purpose=gpu-compute Finally, configure your Pod to use both Node Taint and Pod Affinity to effectively choose what node to run at:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 apiVersion: v1 kind: Pod metadata: name: nginx labels: env: test spec: containers: - name: nginx image: nginx imagePullPolicy: IfNotPresent tolerations: - key: \u0026#34;size\u0026#34; value: \u0026#34;large\u0026#34; operator: \u0026#34;Equals\u0026#34; effect: \u0026#34;NoSchedule\u0026#34; affinity: nodeAffinity: preferredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: purpose operator: In values: - gpu-compute O dabọ\n","permalink":"https://limistah.dev/posts/node-taint-toleration-affinity/","summary":"Use node taint, toleration, and node affinity the right way","title":"Node Taint, Toleration and Affinity"},{"content":"As with many other technologies, VIM can echo messages, if you are coming from a contemporary editor, this can hit differently.\nFirst, the result of all the echoed messages is visible in the status bar at the bottom of the screen,\nSecondly, previous messages can be accessed with the :message command.\nTo echo a message, enter the command mode (ESC or CTRL-C), then type the below command:\n1 :echo \u0026#34;Hello World\u0026#34; This should print out Hello World to the status bar, but would not preserve the message for later reference.\nEcho Commands There are various echoing forms, each with its unique use case.\nEchoing like a comment The form of echoing demonstrated above is meant for unpreserved echo messages,\nThe :echon {expr} command is a better form of it.\n1 :echon \u0026#34;This is a comment message\u0026#34; Highlighting echo results For some reason you might wish to highlight the result of the echo command, the :echoh or :echohl command makes it possible.\n1 :echoh WarningMsg | echo \u0026#34;Don\u0026#39;t panic!\u0026#34; | echo None The WarningMsg is a type of highlight, another possible highlight can be viewed with the :highlight command\nIt is required to :echo None at the end or later when the highlighting process is complete.\nThis preserves the echo messages\u0026rsquo; default (None) state.\nTo provide your custom color, use the highlight command to create a highlight:\n1 :highlight MyGreen ctermfg=green guifg=#00FF00 Use it like the below:\n1 :echoh MyGreen | echo \u0026#34;Green Message\u0026#34; | echo None Echoing Error Messages The :echoe {expr} or :echoerr {expr} command can be used to echo error messages\n1 :echoerr \u0026#34;EOL in the current file\u0026#34; The final message is preserved in the message log.\nSaving the result of the echo command The echom {expr} or echomsg {expr} can be used for messages that should be preserved in the messages log\n1 :echom \u0026#34;Preserved\u0026#34; To view previously saved messages, use :messages command\nEchoing to a window For some echo messages that can be too long for the status bar, the echow or :echowindow command can be used.\n1 :echow \u0026#34;Lorem Ipsum\u0026#34; Echoing to a console echoc can be used to write to the console, although it behaves more like echom when not in GUI mode.\nIf running as a GUI, the message is output to stdout.\n1 :echoc \u0026#34;what are we doing today?\u0026#34; VIM\u0026rsquo;s echo command and Shell\u0026rsquo;s echo command It can be tempting to think the VIM\u0026rsquo;s :echo command is the same as the shell\u0026rsquo;s !echo command.\nOne subtle difference is how they both handle the values that they receive.\nThe :echo command requires Vim native expressions for example to get the current filename do:\n1 :echo expand(\u0026#34;%\u0026#34;) Notice that the expand is a Vim expression command that expands some special characters to some values, in this case, the \u0026ldquo;%\u0026rdquo; is expanded to the current filename\nTo have this same result in a shell command, for example, echo the content of the current file\n1 !echo % The % is replaced with the current filename, this can be useful in other cases e.g. cat the current filename.\nNotice how the expand function is not required to get the desired result.\nWhen using the echo command, use vim\u0026rsquo;s native function for expansion and special effects, in the bash mode, the special characters are expanded before the command is executed.\nNamaste!\n","permalink":"https://limistah.dev/posts/echo-in-vim/","summary":"Echoing like a PRO in VIM","title":"The VIM echo command"},{"content":"Motivation Container images often accept Environment Variables for configuring their environments, for example the redis image accepts REDIS_VERSION for a specific redis version. Below command starts a redis container, passing a desired REDIS_VERSION environment variable.\n1 crictl run -t redis -e REDIS_VERSION=7.2 redis And to start a redis Pod, imperatively:\n1 kubectl run --image=redis --env=REDIS_VERSION=7.2 redis And declaratively:\n1 2 3 4 5 6 7 8 9 10 11 12 13 apiVersion: v1 kind: Pod metadata: name: redis label: app: kv-store spec: containers: - name: redis image: redis env: - name: REDIS_VERSION value: 7.2 In the case of internal images, custom application can require more than enough environment variables to run and can look unmaintainable even with a Pod manifest file:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 apiVersion: v1 kind: Pod metadata: name: redis label: app: kv-store spec: containers: - name: redis image: redis env: - name: REDIS_VERSION value: 7.2 - name: ENV_1 value: VALUE_1 - name: ENV_2 value: VAL_2 A better implementation would be to have a key value pair store for the env section, and that is what config maps are meant for.\nConfigMaps ConfigMaps stand as an insecure secret storage in Kubernetes. To create a ConfigMap imparatively run:\n1 2 3 4 5 6 7 8 9 10 11 12 13 # passing the key/value pair directly, this is not neat kubectl create configmap redis-config \\ --from-literal=REDIS_VERSION=7.1 \\ --from-literal=ENV_1=VALUE_1 \\ --from-literal=ENV_2=VALUE_2 # A neater way is to store the secrets in a file # having the secrets in a file name redis-config.env # cat redis-config.env # \u0026gt; REDIS_VERSION=7.1 # \u0026gt; ENV_1=VALU_1 # \u0026gt; ENV_2=VALUE_2 kubectl create configmap redis-config --from-file redis-config Below manifest file creates a ConfigMap declaratively:\n1 2 3 4 5 6 7 8 apiVersion: v1 kind: ConfigMap metadata: name: redis-config data: REDIS_VERSION: 7.2 ENV_1=VALUE_1 ENV_2=VALUE_2 To inject all of the secrets from the config map into a Pod, use a declarative manifest file like below:\n1 2 3 4 5 6 7 8 9 10 11 apiVersion: v1 kind: Pod metadata: name: redis spec: containers: - name: redis image: redis envFrom: - configMapRef: name: redis-config Below manifest pulls the value of a key from a ConfigMap and injects it as the value of the MAPPED_REDIS_VERSION env. This can be useful when pods share the same ConfigMap but the containers use different names to reference the secret - IMHO this is anti pattern, and unidiomatic.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 apiVersion: v1 kind: Pod metadata: name: redis spec: containers: - name: redis image: redis env: - name: MAPPED_REDIS_VERSION valueFrom: configMapKeyRef: name: redis-config key: REDIS_VERSION Dynamic Secrets In Pods The above methods of injecting ConfigMap is not dynamic, an update to the ConfigMap will not reflect in any RUNNING Pod already using it.\nTo ensure that a ConfigMap is dynamic within a Pod, we can inject it as a volume mount in the container.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 apiVersion: v1 kind: Pod metadata: name: redis spec: containers: - name: redis-container image: redis volumeMounts: - name: config-volume mountPath: /etc/config volumes: - name: config-volume configMap: name: redis-config Test this by running:\n1 2 3 kubectl exec redis -c redis-container -- /bin/sh env # \u0026gt; should log all the environment variable attached to the container Shalom!\n","permalink":"https://limistah.dev/posts/config-maps-k8s/","summary":"Creating and using config maps in Kubernetes","title":"ConfigMaps in K8s"},{"content":"While reading Moving Fast but Smooth, I had a question: would my readers think they need not be in a hurry to get it done? Or just assume complacency should be the norm?\nAt the last Unilag convocation, a student had a perfect score of CGPA (5.0/5.0). This is a great feat, which I would want you to answer: do you think he would achieve this if he was moving slowly?\nI am not a first-class student or even close—aha, think about the IQ of your learned folks, yeah. But I roll with a lot of them, as I love being around people who are better than I am. I noticed two things about them.\nWhen they set out to achieve something, they don’t wait until it happens; they make it happen. And when they make it happen, they ensure they have spent all their best efforts on it.\nProfessionals at the height of their field are often used as a standard and yardstick against others. For them, there is no room for complacency; they must continuously seek out the next challenge and perfectly solve it.\nThey remain proactive in everything they set out to achieve.\nMoving fast but smoothly could make you think, Hey, I am doing good; I only need to take my time to solve this challenge. Yes, that is fine.\nDo not forget that while spending your time, you won’t be missed if the world decides to move on from your current position to another.\nThis might also get you to a place of complacency, where you start thinking, \u0026ldquo;Hey, I am good here\u0026rdquo;, since you stopped being proactive in situations—after all, you needed more time to solve them.\nPeople say, “When we stop growing, we start dying.” When your mind gets stuck and you start feeling extremely comfortable with the current situation, you might be dying silently.\nI do tell people, “Hey, pressure is good!”\nThe most rewarding year of my career was straight out of NYSC. During the program, I would travel from my PPA state down to Lagos for interviews. I felt nothing. I would work in my remote software engineering position and perform at my PPA.\nI also worked at a school where I was a part-time teacher and still gave tutorials to the children of my then-landlord.\nI felt nothing!\nIt was at this time that I started my blog and got my first international writing gig with Smashing Magazine. Gosh, I was thrilled. After completing my NYSC, I worked at Josplay as a remote software engineer, continued writing and interviewing, and ultimately had the opportunity to work with a UK Startup.\nI felt nothing!\nThese were normal situations for me, and I was under the impression that I was under pressure just as a student would.\nBeing a student requires multitasking and becoming a master at it to be successful. Attending classes, submitting assignments, organizing departmental programs, mentoring other students, projects, and events.\nAs a student, I felt almost nothing doing all these; I just wanted to succeed in my program.\nBut one thing stayed constant: I was doing related things, preparing for the next, and taking my time to refine the process till it got better, which is all the idea of moving fast but smoothly.\nDuring my final exam at Laspotech, a friend said, “Ah, after these, I have to rest.” I told him, Nope, after these, I would try to learn something different and take on another challenge. I think what we failed to realize is that the mind and body, like a machine, have their frictions.\nFor a body to move from point A to point B, it needs to overcome a force called inertia. Then it needs to keep accelerating, then stop accelerating until its acceleration returns to zero, and then it stops.\nWhen we start to feel pressure, we try to overcome inertia, such as waking up at 4 a.m. to prepare for work or not getting enough sleep. The body becomes accustomed to it and starts feeling normal again, and when it decides to stop, the body gradually returns to its old state, for example, waking up past 7 a.m. or 8 a.m.\nAnd when you need to do this again, you have to overcome this inertia, which is always very hard. Manufacturing companies always keep their production lines running, partly because the advantage they gain from keeping all the different parts of the machine working uniformly, even when not producing, outweighs the disadvantage of shutting them down just because they are not producing.\nAlways stay under pressure. At all times, seek out new challenges that push your limits. You can take your time to solve the challenge, but keep being proactive in situations that arise.\nThis is designed to help you stay ahead of the curve while still moving smoothly.\nIf you drop below the curve, you will not be missed!\n","permalink":"https://limistah.dev/essays/0006-well-you-wouldnt-be-missed/","summary":"While moving slowly can be rewarding, no one will wait for you in our fast-paced world. You need to press the pedal until the odometer is maxed out. Move faster!","title":"Well, you wouldn't be missed!"},{"content":"I was solving a challenge and got to a point where I had to implement a store that could hold arbitrarily deeply nested objects. The new me, I need to see what others have done.\nI found dot-prop npm package, which solves it quite well.\nBut going through this gist, I found this answer.\nThe author said a \u0026ldquo;functional way to solve it\u0026rdquo;\u0026hellip;\nAnd this right here, is an example of declarative programming.\nI love this!\nShalom\u0026hellip;\n","permalink":"https://limistah.dev/posts/lookup-nested-object-dot-operator/","summary":"\u003cp\u003eI was solving a challenge and got to a point where I had to implement a store that could hold arbitrarily deeply nested objects. The new me, I need to see what others have done.\u003c/p\u003e\n\u003cp\u003eI found \u003ca \n  href=\"https://www.npmjs.com/package/dot-prop\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003edot-prop npm package\u003c/a\u003e, which solves it quite well.\u003c/p\u003e\n\u003cp\u003eBut going through \u003ca \n  href=\"https://gist.github.com/jasonrhodes/2321581\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003ethis gist\u003c/a\u003e, I found \u003ca \n  href=\"https://gist.github.com/jasonrhodes/2321581?permalink_comment_id=1813156#gistcomment-1813156\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003ethis answer\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eThe author said a \u0026ldquo;functional way to solve it\u0026rdquo;\u0026hellip;\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"github.com/shiftyp\" loading=\"lazy\" src=\"/assets/git_gits_permalink_comment_id_1813156.jpeg\"\u003e\u003c/p\u003e\n\u003cp\u003eAnd this right here, is an example of \u003ca \n  href=\"/posts/imparative-declarative-coding/\"\n  \u003edeclarative programming\u003c/a\u003e.\u003c/p\u003e","title":"Dynamically lookup a property in deeply nested object"},{"content":"As a software engineer, I have written a lot of lines of code, and taking hindsight back to the very first day of my career, I have written a log of bad code. I recently published a new version of my react-here-maps library which saw a sprinkle of my improved skill set.\nI had a coding interview where I was tasked to add functionality to support extra data points of the original response and think about adding weeks, months, and years to data that only returned days. The implementation worked but it wasn\u0026rsquo;t what was expected.\nI used a while loop to go through the days which is an object.\n1 2 3 4 type SalesPerDay = { [day: string]: number } const totalPerDay: SalesPerDay = {} I thought, since I already have the days, I could group the days by month/year and sum their values. My implementation looks somewhat like this:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 const modTotalAmtMap = {}; const amountMap = Object.assign({}, totalPricePerDay); while (Object.keys(amountMap).length \u0026gt; 0) { let sortedKeys = Object.keys(amountMap) .map((t) =\u0026gt; new Date(new Date(t).toISOString()).getTime()) .sort() .map((d) =\u0026gt; new Date(d).toISOString()); const start = moment(new Date(sortedKeys[0])).startOf(unit); const end = moment(start).endOf(unit); const totalTrxnInBtw = sortedKeys .filter((k) =\u0026gt; { return moment(new Date(k)).isBetween(start, end); }) .map((k) =\u0026gt; { const val = Number(amountMap[k]); delete amountMap[k]; return val; }) .reduce((acc, amt) =\u0026gt; acc + amt, 0); modTotalAmtMap[start.toISOString()] = totalTrxnInBtw; } Notice how I have created an array out of the original totalPricePerDay and stored it inside of amountMap then sorting the date to get the date ordered earliest first, and moment to determine how many of the items to pick then summing up the values.\nAh! That is too much explaining for such a very simple task.\nDon\u0026rsquo;t be like me, I learned this the hard way. \u0026#x1f60f;\nWhat you have just seen is imperative coding.\nImperative Coding In the world of Kubernetes, you are all about creating objects. There are two ways about it, either you type them as a command or apply them as a manifest file.\nTo create a namespace on Kubernetes you can either run\n1 kubectl create ns [NAME] OR\nCreate a Kubernetes manifest file called ns.yaml then apply it\n1 2 3 4 apiVersion: api/v1 kind: Namespace metadata: name: NAME Which you can then apply with\n1 kubectl apply -f ns.yaml But, there is a big advantage to the manifest file, I can share it with anyone and they only have to type as much code as I would to get the same result, but using the create command, a lot of things can go wrong – incomplete arguments, typos and whatever.\nUsing the create command is called the imperative way of creating objects in K8s, while with the manifest that is declarative style.\nDeclarative thinking Thinking declaratively in coding would mean using the interfaces provided by the framework/language/platform you are on to solve the task at hand.\nIn the Kubernetes example, to use the command means, I have to string them up in the order I see fit whereas, there is a standard structure that I could use to achieve similar results.\nIn the problem I was solving, I needed to use a while loop to go through the list, I could use a map, forEach and other constructs, I need not use a moment, there is a possibility that what a moment would do, the native Date object would have done it. Above all, I need not come up with my unique solution, all I needed was to take a second look at the code and check the APIs to understand what is attainable before venturing out to seek other libraries.\nBut imperative coding doesn\u0026rsquo;t mean you need not bring in libraries, check out this library ts-patterns it attempts to remove conditionals from our code base with understandable APIs. I for one now find conditionals hard to understand – as I told a colleague, your logic should not always require much brain power to process.\nSo, this would have been an improvement:\n1 2 3 4 5 6 7 8 // Adjust the date based on the frame if (filter === \u0026#39;monthly\u0026#39;) { date = new Date(sales.date).toLocaleDateString(\u0026#39;en-US\u0026#39;, { year: \u0026#39;numeric\u0026#39;, month: \u0026#39;numeric\u0026#39; }); } else if (filter === \u0026#39;yearly\u0026#39;) { date = new Date(sales.date).toLocaleDateString(\u0026#39;en-US\u0026#39;, { year: \u0026#39;numeric\u0026#39; }); } else { date = new Date(sales.date).toLocaleDateString(\u0026#39;en-US\u0026#39;); } So clean and expressive, right?\nThat is the power of declarative coding. You don\u0026rsquo;t need to explain it that much almost everyone gets it!\nSalut!\n","permalink":"https://limistah.dev/posts/imparative-declarative-coding/","summary":"Choose to be clearity over ambiguity","title":"Imparative and Declarative coding?"},{"content":"Byte masking is a deep CS concept reserved for the nerds. Here we will attempt to dissect the topic and provide a relatable experience for everyone.\nWelcome\u0026hellip;\nMasking and its Relation to CS Masking is a process of concealing information. Take for example having a string \u0026quot;A\u0026quot; but revealing \u0026ldquo;X\u0026rdquo; to others such that only those with the information on how to get the hidden value can retrieve it.\nA byte is a group of bits(1 and 0) usually eight in number. Such that 00000000 becomes a byte but the individual zeros are known as bits.\nIn CS bitmasking is a process of manipulating the bits of computational data. For example, converting 00000000 to 01010101. In CS, it mostly denotes what bit you want to keep, and what bit you wish to clear. in our case, we have divided the byte into pairs and cleared the first bit 00|00|00|00 =\u0026gt; 01|01|01|01.\nMasking Operators Masking bits is done by the use of masking operators, taking from the byte operators:\nThe Bitwise OR | (a single pipe character) The Bitwise AND \u0026amp; (a single ampersand character) The Bitwise XOR ^ (a single caret character) The Left Shift \u0026laquo; (a double less than character) The Right Shift \u0026raquo; (a double greater than character) Masking operations examples OR The rule for bitwise OR is as follows:\nIf at least one of the corresponding bits is 1, the result bit is set to 1. If both corresponding bits are 0, the result bit is set to 0. 1 2 3 4 10101010 (binary number) | 11001100 (binary number) __________ 11101110 (result) AND Here\u0026rsquo;s how the bitwise AND operator works:\nIf both bits in the operands are 1, the result will be 1. If at least one of the bits is 0, the result will be 0. 1 2 3 4 10101010 \u0026amp; 11001100 ----------- 10001000 XOR Here\u0026rsquo;s how the XOR operator works:\nIf the bits in the operands are different (one is 0 and the other is 1), the result will be 1. If the bits in the operands are the same (both 0 or both 1), the result will be 0. 1 2 3 4 10101010 ^ 11001100 ----------- 01100110 Left Shift Here\u0026rsquo;s how the left-shift operator works:\nEach bit in the binary representation of the operand is shifted to the left by a certain number of positions. Zeros are filled in from the right, and the bits that are shifted beyond the leftmost position are discarded. The result is obtained by multiplying the original value by 2 raised to the power of the specified shift count. 1 2 Original Value: 00101010 (42 in decimal) Left Shift by 2: 10101000 (168 in decimal) Each bit in the original binary value 00101010 is shifted to the left by 2 positions. Zeros are filled in from the right, and the result is 10101000, which is equivalent to 168 in decimal.\nRight Shift Here\u0026rsquo;s how the right shift operator works:\nEach bit in the binary representation of the operand is shifted to the right by a certain number of positions. Depending on the type of right shift (logical or arithmetic), zeros or the sign bit (the leftmost bit) are filled in from the left. The bits that are shifted beyond the rightmost position are discarded. The result is obtained by dividing the original value by 2 raising to the power of the specified shift count (for logical right shift) or retaining the sign bit (for arithmetic right shift). 1 2 Original Value: 10101010 (170 in decimal) Right Shift by 2: 00101010 (42 in decimal) There are two types of right shift:\nLogical Right Shift (\u0026gt;\u0026gt;\u0026gt;): Zeros are filled in from the left, and the sign bit is always 0. This type of shift is commonly used in programming for unsigned integers. Arithmetic Right Shift (\u0026gt;\u0026gt;): Zeros or the sign bit are filled in from the left, depending on the sign bit of the original value. This type of shift is used in signed integer arithmetic to preserve the sign. How masking helps our daily lives When you shouldn\u0026rsquo;t consider masking Adios\n","permalink":"https://limistah.dev/posts/byte-masking/","summary":"The idea of byte masking helps us achieve abstractions from the binary concept of computers to usable technology. This post helps to solidify byte masking using operators and gives examples of why we must be aware of this concept in our everyday software engineering craft.","title":"Byte Masking the ins and out"},{"content":"In Ruby, the if statement looks like this\n1 2 3 4 5 val = 1 if val == 1 p \u0026#34;Equality Checked!\u0026#34; end And for if else\n1 2 3 4 5 6 7 val = 2 if val == 1 p \u0026#34;Equality Checked!\u0026#34; else p \u0026#34;Equality Unchecked!\u0026#34; end And for if, else if, else\n1 2 3 4 5 6 7 8 9 val = 2 if val == 1 p \u0026#34;Equality Checked!\u0026#34; elsif val == 2 p \u0026#34;Equality Middle Checked!\u0026#34; else p \u0026#34;Equality Unchecked!\u0026#34; end Also, remember that everything in ruby returns a value, so your if statement can return a value that could be stored in another variable.\n1 2 3 4 5 6 7 8 9 10 val = 2 # store the returned value from the if statement ret_val = if val == 1 p \u0026#34;Equality Checked!\u0026#34; else p \u0026#34;Equality Unchecked!\u0026#34; end p ret_val # =\u0026gt; \u0026#34;Equality Unchecked!\u0026#34; Dhanyavaad! 🙇\n","permalink":"https://limistah.dev/posts/ruby-if-conditions/","summary":"If statement the ruby way.","title":"Ruby - if statement?"},{"content":"I had a conversation with a friend around 2015 where we discussed setting up a shoe retail business from the hood (Ajegunle) using e-commerce websites. We thought we could showcase the embedded talents of the local shoemakers; fortunately, we sold two pairs of the 15 we invested in.\nAfter receiving the first batch, we held onto them. I was a student at Lagos State Polytechnic. My friend managed the deliveries, while I handled the online operations.\nPreviously, we had successfully retailed acne creams; we wanted to move faster.\nWe got innovative and decided to rent an outlet in the hood; obviously, we couldn’t afford it, so we had to return to our online store, hoping to get more customers.\nThen school came looking for me; it was the office for my friend. Unconsciously, we both overlooked the business and our plans to build an income stream before we could become responsible adults.\nWe were fast!\nLeft to myself, I have had a tale of speed in my life’s journey, and I have witnessed more of this in others. One question I ask is: once you get to that finish line, what next?\nImmediately after I learned how to drive, my brother, who was my driving instructor, said, “Notice the guys drifting past you; don’t be bothered, because there would be at most a 10-minute difference between your and their time of arrival.” As a trained scientist, I constantly analyze this statement and witness it to be accurate.\nThe drivers often prioritize getting to their destination faster over their own safety. My brother would also point out that we started our journey from different places, we were going to various locations, and we left at different times, so we must arrive at different times. So, why should I run after them?\nAnd bringing this to life: Why should you run after other people?\nAt its core, the saying “Slow is smooth, and smooth is fast” emphasizes the importance of accuracy, consistency, and a controlled pace in executing tasks. Contrary to popular belief, the fastest route to success isn’t always about rushing headlong into tasks. It’s about maintaining a rhythm, a smoothness in operation that naturally leads to increased speed and efficiency. In Scaling Up, we call this a cadence.\n- George Morris on Slow is Smooth Smooth is fast\nAs humans, we always desire to be ahead, and this increases our oversight of things we ought to pay attention to. As a Muslim, my prophet was recorded to have said that the best deed in the sight of Allah is the one that is short and consistent.\nSlow is smooth The convenience and details a slow person can gather are higher than those of their faster peers. Consider reading an exam question; you don’t want to read too fast, so you might miss a detail. Slow is smooth.\nSmooth is fast Since a process is slow, it is easier to replicate; you don’t need speed or accuracy to achieve it. You need just accuracy, and accuracy becomes better at slower speeds.\nThere is more time to pay attention to situations and enough time to correct bad attempts until they are perfect. When it comes out smoothly, you forget how long it took; you remember how great it has turned out to be.\nThis narrative, Slow is Smooth, Smooth is Fast, is an element of the military. Removing the idea of the soldiers completing too many tasks, but instead completing fewer tasks, accurately.\nConsidering how busy life is as an adult, weighing importance and necessity, you don’t need to do many things quickly.\nYou need to do a few things correctly.\nI would appreciate the work of a subordinate who came in late but needed almost no corrections more than one who came in earlier but turned out to be garbage. Or a subordinate who completes the task and continues to refine it till it becomes better.\nGrowth is necessary, but we don’t need to grow that fast. Earning fat pay is necessary, but it doesn’t have to come from the first few years of your career. Take the time to put in refinements and do small things correctly but consistently.\nInstead of focusing on how fast you can get, why not consider how accurate you could be?\nQuality over quantity, each time!\n","permalink":"https://limistah.dev/essays/0005-moving-fast-but-smooth/","summary":"A car that swifts at 220 km/h can crash in less than a second if it drifts wrongly. And a car moving at 20 km/h? A wrong drift is amendable. It is about getting home safely, then getting home fast!","title":"Moving Fast But Smooth"},{"content":"I recall back in 2019, I mentioned that this decade is for marriage for my folks. Oh yeah, most of my peers are now adults; 🎉, some are married, some have children, some are about to, and others are undetermined. I am happy for everyone and hope to see us continue this journey of life with more swag and beauty.\nI always reminisce about my aspirations and goals in life, as I understand and believe that aside from chasing and getting money, my life should be about something. If you haven’t defined it yet, it is best to define it now.\nA life with purpose is one with meaning; we recall Alhassan Dantata for his wealth, we remember Moshood Abiola for his contributions, and we recall Obafemi Awolowo for his advocacy of free education. You can remember your grandparents for their purposes. Purpose makes us more memorable as we journey out of life.\nHaving a purpose that we run after in our lives requires some special attention, one that is expensive to get as an adult, as you trade your limited time for money to earn a living—even if you run a business.\nYour relatives, friends, spouse, parents, and internet all want your attention at the same time, which is almost impossible, but we can always make a trade-off as to which to give priority. So, then I ask the question: what is essential and what is necessary?\nWe did some scale of preference in our secondary school economics and opportunity cost. It is not limited to buying an item; it is essentially about making better decisions at every point in life.\nYou have NGN200, but you need to buy sandals for school, which cost NGN150; a mathematical set costs NGN200; a new jacket costs NGN100; or a new textbook costs NGN250. What would you do?\nBringing it to our current state, you have work, your spouse, your notifications, your parents, and your friends wanting to get your attention. What would you do?\nNotice how money became attention and commodities became people.\nFor something to be necessary, I can’t live without it; if I don’t do it, I might die, for instance, from sleep. If I have a sleep schedule but my work wants to interfere with it, I would rather sleep, as my health is more important than anything else.\nIf I am working and my parents call, I would rather prioritize the call; they are necessary, as I am responsible for their well-being. If I am playing and work shows up, I would rather head off for work, as I won’t survive without my wages.\nStill, some things are essential: working is necessary, but working hard is important; visiting my parents or attending to their needs is essential, but giving them comfort is important.\nHaving a good time with my family is essential, but spending quality time with them every day is even more critical. Seeking growth is necessary, but making a substantial amount of money is essential.\nEssential things are things that could exist even without you, but because you are there, you must do them. Kids can get quality time without me, but since I am their father, I have to give it to them. My friends can play out without me, but because I am their friend, I have to go out with them.\nMaking more money can exist without my growth, as I can increase my earnings by switching to another field; however, growth is necessary if I am to maintain the same field of work.\nYou might have school, work, business, and family. Switching between these roles without causing damage to others requires that you make the best decision on what to do right now and what to do later.\nLife itself doesn’t let you make an easy decision; you might have to submit an assignment today, and your boss needs that proposal by morning, yet your wife needs your help, and there is a delivery for new packages at the business. '\nYou need to decide what to do and in what order to do them, and accept the cost of not getting some things done on the spot—an opportunity cost in economics.\nI am a fan of multitasking; nature itself is a master at it, and you really can’t escape it in life. Although you can only focus on one thing at a time, you do have the ability to have many things running simultaneously. As we grow older, from teenagers to adults, the responsibilities continue to pile up.\nThis is a tasking mental model that we pick up subconsciously, I believe. Imagine our parents and how many things they manage even to this day. They know about every relative, the whole history of their career or business, and their children’s attributes.\nAnd I want to believe they mastered knowing what is necessary and what is essential at every point in time. It is innate in humans, and I think we will adapt as well. Just a heads-up!\nI would love to hear how you make your choices when presented with myriad seemingly necessary situations. For me, I order them by health first, then shelter, then food, then energy, then money, then education or knowledge. What is it for you?\n","permalink":"https://limistah.dev/essays/0004-becoming-adult-e-sense/","summary":"As adults, with too many things to do in a day, we all suffer from conflicting interests in our limited time. Let\u0026rsquo;s explore how to integrate our daily routines with our life\u0026rsquo;s purpose.","title":"Becoming Adult-e-sense"},{"content":"I have a vivid memory of watching movies at neighboring homes with my friends. I would see a character and wish to be like that when I grow up. One that I can correctly recall is Amitabh Bachchan. I remember his selflessness in his movies from the late 90s and early 2000s.\nI also watched a few villains and decided never in my life would I want to be this kind of person growing up. I believe I made these decisions that have had a subconsciously profound impact on who I am today.\nAt this point in life, I am forging forward in my career, and my question remains: how can I be that person? Taking after their footsteps, attempt to replicate their achievements and earn similar accolades. I guess I want what other people want.\nIn November 2022, I decided that 2023 would solely focus on inner reflections. I am not sure if you would agree with me, but it is one of the key traits of high achievers. What reflection does is take you out of your reality, turning you into an observer of yourself.\nMore like having a big lamp over a piece of paper revealing all its printed content. Earlier this year, Bee (a good friend of mine) told me in one of our deep discussions, “It is good to take a step backward”.\nMy reflections gave me a lot of observations, but all led to a single trait: consistency. Most of what we are today are things that we have consistently done over a period of time. A student can become a graduate by consistently passing their exams. I am a software engineer because I consistently write lines of code every day.\nI realized things I could have done more often over the past 3 years of my career that could amplify the results I have today. I am grateful; don’t mind me. How can I, as a human, not fulfill this verse of the Qur’an 16:8:\nIf you tried to count Allah’s blessings, you would never be able to number them. Surely Allah is All-Forgiving, Most Merciful.\nI could have written more, spoken more at tech conferences, gotten enough tech certifications, and made more open-source contributions. I could have expanded my network far beyond what I have today.\nAll these are great realizations due to consistency, but I also realized that I could have been consistent only if I knew who I was. Recall that I had aspirations when I was younger to have some traits; trust me, I now possess those traits.\nI do things people never expected other people to do for them (no brag), and I make decisions that take more people into consideration than average people would. I have other traits too, 😉.\nBeing consistent means having discipline; regardless, some things must happen. To be a disciplinarian does not entail you have to break too much; it entails you know what you are capable of and what can actually break you.\nIf your body doesn’t work late in the night, you really are doing a disservice to yourself by trying to force it through discipline to make it work at night.\nIf you realize that your body works well in the morning and needs to sleep during the night, discipline means that regardless of what is happening, you must write a paragraph every morning when you wake up. This means taking into consideration who you are and what you need to do.\nFor me, consistency means understanding my atmosphere, what prevents me from doing what I need to do, and what motivates it. I really would love to eliminate distractions, but as I have grown older and tried, I have realized I should work around distractions, not eliminate them.\nA good way to work around distraction is to find when it is minimal. If you can control the velocity of the distraction, that would be a great advantage, as you can tell the distraction to go quiet when you need it, and afterward, it can continue to be what it wants to be.\nPlease agree with me that character emulation really doesn’t work as expected—for me at least. I realize that, as much as I have wanted to be like some certain figure, I am not like them; I am me (in Lil Wayne’s voice).\nMy life is unique (Victony: My life is a different story). My experience is unique, and my interactions with all the elements of life are unique. Moving forward, I want to remain myself, becoming a better version of myself every future second.\nGenerally, it has been a very useful experiment for me. and I am very glad that I attempted it. As we move on to the future, I hope to witness more consistency in things that will give me the results that I yearn for.\nGoing forward, I don’t want to be like anybody else; I want to be among the few, not among the many!\nAdiós\n","permalink":"https://limistah.dev/essays/0003-what-do-you-want-moving-forward/","summary":"Over time, who we are is a reflection of what we have done. Who do we want to be? We do not need to change anything from what we do; we can choose the group that we want to belong to moving forward","title":"What do you want moving forward?"},{"content":"Part of the operators we get introduced to when learning to program is Bitwise Operators, examples are:\nThe Bitwise OR | (a single pipe character) The Bitwise AND \u0026amp; (a single ampersand character) The Bitwise XOR ^ (a single caret character) Each of these has its usage, a refresher can be demonstrated considering these two variables foo=1 and bar=0\nFor The Bitwise OR(|) Operator 1 2 3 const foo = 1, bar = 0 console.log(foo | bar) -\u0026gt; 1 For the bitwise AND(\u0026amp;) operator 1 2 3 const foo = 1, bar = 0 console.log(foo \u0026amp; bar) -\u0026gt; 0 For the bitwise XOR(^) operator 1 2 3 const foo = 1, bar = 0 console.log(foo \u0026amp; bar) -\u0026gt; 0 This seems pretty basic until you understand it is not.\nWhat the heck is byte masking? Byte masking is a process of flipping the smallest unit of computing value called bits.\nSome CS Background Computer as you interact with it is basically 1s and 0s on the lowest level. This text you are reading is represented as a bunch of 1s and 0s inside the computer. These values represent the state of a very tiny piece of a device called a transistor.\nTransistors as electronic devices are very unique and useful, they behave like light bulbs (i.e. they can be turned on and off), what makes them very useful to us today is that they can maintain their state(either on or off) over a very long time. Each of these states is represented as either on(0) or off (1).\nBits to Bytes After the advancement of transistors, the next phase is on how to make them useful for everyday usage(what technology is meant for), which is how to make transistors as a storage devices. But we can\u0026rsquo;t store the Alphabet as on and off or 1 and 0, the byte was invented.\nA byte is a representation of a group of bits. Eight(8) bits would produce One(1) byte. Storing this on transistors means that transistors have to be grouped, and each group would have an identity. For example, if we have 32 transistors, that is 32 bits, and can be further converted to 32 / 8 = 4bytes, another example is if we have 32000 bits we would have 4000bytes. The 4000bytes can be simplified by dividing by 1000 to create a kilo version, hence 4000byes/1000=4Kbytes\nBytes to ASCII Code With computers stuck at 1s and 0s, it makes more sense to stick to just the base 2 numbering system for computer arithmetic operations. For example, 1 + 1 in base 2 equals 0 (add both numbers, divide by 2 then take note of the remainder after the division), and 1+0 in base 2 equals 1 (subtract both numbers, then divide by 2, then take note of the remainder after the division).\nWith these genius concepts, invention went further by assigning numbers to every number, character, and symbol ever known to man. This assignment is called ASCII character code. A(capital alphabet) is a different character from a(smaller alphabet) and both have unique ASCII numbers. For A the ASCII code is 065 and ais 097.\nRepresenting Characters Representing a character on a transistor(in bits) becomes easier, convert the code from base 10 to 2. For A the base 10 value is 065, while the base 2 value is 01000001 and for the small a with a base 10 value of 097 the base 2 value is 01100001. Numbers written in base 10 are called Decimal Numbers, while numbers written in base 2 are called Binary numbers.\nRepresenting Words Since we can store numbers, characters, and symbols, we should be able to store words. Words are a group of letters and somehow letters, and alphabets, it is safe to use string, so a string of numbers, alphabets, and symbols.\nThis can be easily done by taking a consecutive byte(recall that this means 8 units of bits) until all the characters in the string are represented. For example, to represent hello we would take the first byte and fill it with 01101000 (104 in ASCII) for h, then 01100101 (101 in ASCII) for e, then 01101100 (108 in ASCII) for l, then 01001100 (076 in ASCII) for l, and 01101111 (111) for o.\nGrouping this together it forms a string of 0s and 1s like this\n0110100001100101011011000110110001101111.\nWhat Is Byte Masking? Byte masking is data manipulation at the bit level. What is happening is based on the kind of byte masking operation, we are instructing the transistor to switch to another state or maintain their state. For example, the binary code for the number 1 is 1, and for 2 is 10, and a bitwise OR operator on both numbers would give 3.\n1 2 3 const foo = 1 // 01 in binary const bar = 2 // 10 in binary foo | bar // =\u0026gt; 3 , 11 in binary What is happening here is in the way the bitwise OR operator works. It adds up the binary numbers, divides the result by 2, and records the remainder. Here if we do a right-to-left addition, 1+0 would give 1 divided by 2 gives zero but left with 1, and to the right we have the same operation we would end up with 1 as well. But, 11 is a binary representation of 3 in decimal numbers.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 0 1 | 1 0 ------ 1 1 # Taking the top right and the bottom right 1 + 0 = 1 1 / 2 = 0 remainder 1, take the remainder as the answer # Taking the top left and the bottom left 0 + 1 = 1 1 / 2 = 0 remainder 1, take the remainder as the answer #------ 11 in base 2 is 3 in base 10 1 | 2 returns 3 Be aware that 1 is not stored as 1 in computers but as 00000001 same for 2, it is rather stored as 00000010 for consistency against larger values.\nMasking Operators To better understand byte masking operations, take a look at what each masking operator would do for you.\nThe Bitwise OR Operator As shown earlier, this operator returns the result of adding two bytes together. In the case of 1 and 2, we added 00000001 and 00000010 together to result into this 00000011.\nIf the addition seems more arithmetic here is a better way to understand this:\n1 2 3 4 // OR Operator 00000001 00000010 = 00000011 As long as there is 1 in any of the values we are comparing, the result must return 1.\nThe Bitwise AND Operator This is the reverse of the bitwise OR operator. Instead of taking the remainder, we take the result of the division by 2.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 # 01 \u0026amp; 10 0 1 \u0026amp; 1 0 ------- 0 0 # Taking the top right and the bottom right 1 + 0 = 1 1 / 2 = 0 remainder 1, take the result of the division as the answer # Taking the top left and the bottom left 0 + 1 = 1 1 / 2 = 0 remainder 1, take the result of the division as the answer ----- 00 in base 2 is 0 in base 10 1 \u0026amp; 2 returns 0 A better way to understand this without the math\n1 2 3 4 5 6 7 8 9 # AND Operator +-------------------------------+ | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | +---+---+---+---+---+---+---+---+ \u0026amp; | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | +-------------------------------+ = | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | +-------------------------------+ As long as there is a 0 in any of the columns we are comparing, the result must return 0.\nThe Bitwise XOR Operator For the XOR operator, it checks if the remainder after the division of the two values is equal to 0, if it is, then the result is the same as the division (0, mostly for 0 + 1, 1 + 0 operations) , if the remainder after the division is 1 then the result is the remainder (0, mostly for 1 + 1 operations).\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 # XOR Operator 101 | 110 1 0 1 | 1 1 0 ------- 0 1 1 Taking the top right and the bottom right 1 + 0 = 1 1 / 2 = 0 remainder 1, take the remainder as the answer since the result is 0 Taking the top middle and the bottom middle 0 + 1 = 1 1 / 2 = 0 remainder 1, take the remainder as the answer since the result is 0 Taking the top left and the bottom left 1 + 1 = 2 2 / 2 = 1 remainder 0, take the result of the division as the answer since the remainder is 0 ----- 11 in base 2 is 3 in base 10 1 | 2 returns 3 Without the arithmetic:\n1 2 3 4 5 6 7 8 # 10000101 ^ 00000110 +-------------------------------+ | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | +---+---+---+---+---+---+---+---+ ^ | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | +-------------------------------+ = | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | +-------------------------------+ The Bitwise NOT operator This operator basically negates the value of each bit. If a bit is 0 it becomes 1 and if it is 1 it basically becomes 0. For example:\n1 2 3 4 # NOT Operator ~ 101 ----- = 010 Also,\n1 2 3 4 # NOT Operator ~ 010 ----- = 101 Other Operators There are still two more operators, the Left Shift Operator and the Right Shift Operator. Both try to inverse(change to the other value)\nVoila!\n","permalink":"https://limistah.dev/posts/byte-operators/","summary":"\u003cp\u003ePart of the operators we get introduced to when learning to program is Bitwise Operators, examples are:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eThe Bitwise OR  \u003ccode\u003e|\u003c/code\u003e (a single pipe character)\u003c/li\u003e\n\u003cli\u003eThe Bitwise AND \u003ccode\u003e\u0026amp;\u003c/code\u003e (a single ampersand character)\u003c/li\u003e\n\u003cli\u003eThe Bitwise XOR \u003ccode\u003e^\u003c/code\u003e (a single caret character)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eEach of these has its usage, a refresher can be demonstrated considering these two variables \u003ccode\u003efoo=1\u003c/code\u003e and \u003ccode\u003ebar=0\u003c/code\u003e\u003c/p\u003e\n\u003ch3 id=\"for-the-bitwise-or-operator\"\u003eFor The Bitwise OR(|) Operator\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-0-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-1\"\u003e1\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-2\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-2\"\u003e2\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-3\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-3\"\u003e3\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-js\" data-lang=\"js\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003efoo\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003ebar\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003efoo\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"nx\"\u003ebar\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch3 id=\"for-the-bitwise-and-operator\"\u003eFor the bitwise AND(\u0026amp;) operator\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-1-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-1\"\u003e1\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-2\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-2\"\u003e2\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-3\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-3\"\u003e3\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-js\" data-lang=\"js\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003efoo\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003ebar\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003efoo\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u003c/span\u003e \u003cspan class=\"nx\"\u003ebar\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch3 id=\"for-the-bitwise-xor-operator\"\u003eFor the bitwise XOR(^) operator\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-2-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-1\"\u003e1\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-2\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-2\"\u003e2\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-3\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-3\"\u003e3\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-js\" data-lang=\"js\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kr\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003efoo\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003ebar\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003efoo\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u003c/span\u003e \u003cspan class=\"nx\"\u003ebar\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003eThis seems pretty basic until you understand it is \u003cstrong\u003e\u003cem\u003enot\u003c/em\u003e\u003c/strong\u003e.\u003c/p\u003e","title":"What is Byte Masking and how useful is it?"},{"content":"Since everything is an object in Ruby having a functionality that can duplicate objects is not a bad idea.\nRuby ships with two methods for making copies of an object: the dup method and the clone method.\nIn Ruby, all variables hold a reference to an object. In a case where a section of a code modifies an object that is not meant to be modified, it is ideal to make a copy of that object to be used in that section of the code, protecting the integrity of the copied object.\n1 2 3 4 5 6 7 8 9 10 11 12 13 # initial value of str str = \u0026#34;this is a test string\u0026#34; # accepts a string def modifyAnyString (strVar) # string is replaced with another string changing the value strVar.replace(\u0026#34;this is a modified version of strVar\u0026#34;) end # call the modify string with the str variable modifyAnyString(str) puts str # outputs: this is a modified version of strVar In some languages like C# and Java, this is called passing by value or dereferencing.\nThe dup method Take this multiverse object initialized below:\n1 multiverse = Object.new We can pass multiverse to any method that has a write operation, this would affect the object.\n1 2 3 4 5 def getObjectID (obj) obj.object_id # returns the object ID of the passed object end puts getObjectID(multiverse) == multiverse.object_id # true To avoid sending in the exact object by reference use the Object.dup method to create a copy of an object.\n1 puts getObjectID(multiverse.dup) == multiverse.object_id # false Now, it is safer to pass the duplicated variable to a method. This would protect modifying multiverse objects in the getObjectID method if the method is not meant to do any write operation on the object.\nNote: If an object is frozen, the returned object will remain frozen, even after it has been duplicated.\n1 2 3 4 5 6 7 8 9 10 11 dup_multi = multiverse.dup # freeze the dup_multi dup_multi.freeze frz_dup_multi = dup_multi.dup # false original multiverse has not been frozen puts multiverse.frozen? # true The dup_multi was frozen before duplication puts frz_dup_multi.frozen? The copy method Copying an object is almost the same as duplicating an object, the only difference is that, when copying, if the object is frozen, the copied object becomes unfrozen.\n1 2 3 4 5 6 7 8 9 10 11 copy_multi = multiverse.dup # freeze the dup_multi copy_multi.freeze frz_copy_multi = copy_multi.dup # false original multiverse has not been frozen puts multiverse.frozen? # false The copy_multi was frozen before duplication puts frz_copy_multi.frozen? This can be useful to create a copy of the same object that is not restricted either by the function caller or by developers who want to ensure that a method consistently works with unfrozen objects.\nSalut! 🙇\n","permalink":"https://limistah.dev/posts/create-multiple-copies-of-an-object-ruby/","summary":"\u003cp\u003eSince everything is an object in Ruby having a functionality that can duplicate objects is not a bad idea.\u003c/p\u003e\n\u003cp\u003eRuby ships with two methods for making copies of an object: the \u003ccode\u003edup\u003c/code\u003e method and the \u003ccode\u003eclone\u003c/code\u003e method.\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eIn Ruby, all variables hold a reference to an object. In a case where a section of a code modifies an object that is not meant to be modified, it is ideal to make a copy of that object to be used in that section of the code, protecting the integrity of the copied object.\u003c/p\u003e","title":"Creating multiple copies of objects in Ruby"},{"content":"Usually, programming languages have methods for printing out variables. Ruby is not an exception. We will explore the 3 popular methods for printing variables in the Ruby Programming language.\nThe print method The way print(var) works is basically converting its value to a string by calling the to_s method on the object(everything is an object in Ruby) before printing the value and returning nil to its caller.\n1 2 num = 123 print(num) # -\u0026gt; 123 =\u0026gt; nil The print method can be easily used for concatenating strings\n1 2 3 4 5 6 7 8 9 10 11 num = 123 name = \u0026#34;Aleem\u0026#34; print \u0026#34;The name of the boy is \u0026#34; print name print \u0026#34;, and his tag ID is: \u0026#34; print num print \u0026#34;.\u0026#34; # outputs everything on a single line # -\u0026gt; The name of the boy is Aleem, and his tag ID is: 123 Having print as the last operation in a method should be avoided if returning nil is not the desired value\n1 2 3 4 5 6 7 def check_print print \u0026#34;This should print without a new line\u0026#34; end val = check_point # -\u0026gt; This should print withouit a new line # Now p val would return nil The puts method puts method is not so different from the print method except for two scenarios:\nputs adds a new line character at the end of the printed value 1 2 3 4 5 6 7 print \u0026#34;hello World\u0026#34; # -\u0026gt; Hello World # -\u0026gt; nil puts \u0026#34;Hello world\u0026#34; # -\u0026gt; Hello world # -\u0026gt; # -\u0026gt; nil puts prints each element in an array on a new line 1 2 3 4 5 6 7 8 9 10 11 12 13 arr = [1,2,3,4,5,6] print arr # -\u0026gt; [1,2,3,4,5,6] # -\u0026gt; nil puts arr # -\u0026gt; 1 # -\u0026gt; 2 # -\u0026gt; 3 # -\u0026gt; 4 # -\u0026gt; 5 # -\u0026gt; 6 # -\u0026gt; nil The p method This method can be seen as a debugging tool. It prints more than just the value of a variable, it can print the memory, the object it belongs to. A good name befitting the p method is the variable inspection method.\n1 2 3 4 5 p STDERR # -\u0026gt; \u0026lt;IO:\u0026lt;STDERR\u0026gt;\u0026gt; puts STDERR # -\u0026gt; \u0026lt;IO:0x000000013f888e58\u0026gt; Notice that above we have printed the value of STDERR to the console. using the p method, the module that the constant belongs as well as its name is returned, while puts only returns the module and memory address of the STDERR constant.\nShalom 🙇\n","permalink":"https://limistah.dev/posts/when-to-use-puts-print-p-in-ruby/","summary":"\u003cp\u003eUsually, programming languages have methods for printing out variables. Ruby is not an exception. We will explore the 3 popular methods for printing variables in the Ruby Programming language.\u003c/p\u003e\n\u003ch3 id=\"the-print-method\"\u003eThe \u003ccode\u003eprint\u003c/code\u003e method\u003c/h3\u003e\n\u003cp\u003eThe way \u003ccode\u003eprint(var)\u003c/code\u003e works is basically converting its value to a string by calling the \u003ccode\u003eto_s\u003c/code\u003e method on the object(everything is an object in Ruby) before printing the value and returning \u003ccode\u003enil\u003c/code\u003e to its caller.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-0-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-1\"\u003e1\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-2\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-2\"\u003e2\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-ruby\" data-lang=\"ruby\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003enum\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e123\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003enum\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"c1\"\u003e# -\u0026gt; 123 =\u0026gt; nil\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003eThe \u003ccode\u003eprint\u003c/code\u003e method can be easily used for concatenating strings\u003c/p\u003e","title":"When to use puts, print, and p in Ruby"},{"content":"The error:\n1 tar (child): xz: Cannot exec: No such file or directory Is majorly an issue with the xz command not found on the host machine.\nTo verify, run:\n1 whereis xz Fix To fix, use the installation command for your Linux distribution:\n1 2 3 4 sudo apt-get install xz-utils # Debian / Ubuntu sudo yum install xz # RHEL / CentOS sudo zypper in xz # OpenSuSE sudo pacman -S xz # Arch Linux Then untar again with:\n1 tar -xf path_to_file.tar.xz Ref: S/O\nDhanyavaad! 🙇\n","permalink":"https://limistah.dev/posts/tar-child-xz-cannot-exec/","summary":"\u003cp\u003eThe error:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-0-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-1\"\u003e1\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003etar \u003cspan class=\"o\"\u003e(\u003c/span\u003echild\u003cspan class=\"o\"\u003e)\u003c/span\u003e: xz: Cannot exec: No such file or directory\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003eIs majorly an issue with the \u003ccode\u003exz\u003c/code\u003e command not found on the host machine.\u003c/p\u003e\n\u003cp\u003eTo verify, run:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-1-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-1\"\u003e1\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ewhereis xz\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch3 id=\"fix\"\u003eFix\u003c/h3\u003e\n\u003cp\u003eTo fix, use the installation command for your Linux distribution:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-2-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-1\"\u003e1\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-2\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-2\"\u003e2\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-3\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-3\"\u003e3\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-4\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-4\"\u003e4\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo apt-get install xz-utils          \u003cspan class=\"c1\"\u003e# Debian / Ubuntu\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo yum install xz                    \u003cspan class=\"c1\"\u003e# RHEL / CentOS\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo zypper in xz                      \u003cspan class=\"c1\"\u003e# OpenSuSE\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003esudo pacman -S xz                      \u003cspan class=\"c1\"\u003e# Arch Linux\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003eThen \u003ccode\u003euntar\u003c/code\u003e again with:\u003c/p\u003e","title":"tar (child) xz Cannot exec No such file or directory"},{"content":"A onliner 1 for f in *.old; do mv \u0026#34;$f\u0026#34; \u0026#34;${f%.old}.new\u0026#34;; done How??? To change the name of a file on Linux/Unix use the mv command\n1 mv currentfilename newfilename If there is a file named aleemisiaka.old and want to rename to aleemisiaka.new\nWe can store the filename to a variable with export FILENAME=aleemisiaka\nAnd use the variable name in the mv command\n1 mv \u0026#34;$FILENAME\u0026#34;$ \u0026#34;$FILENAME.new\u0026#34; This renames the file from aleem-isiaka.old to aleemisiaka.old.new\nTo ensure we end up with aleemisiaka.new and not aleemisiaka.old.new we could use parameter expansion.\necho \u0026quot;${FILENAME%.old}\u0026quot; would give just aleemisiaka without the old.\nNow append the new extension with echo \u0026quot;${FILENAME%.old}.new\u0026quot; which gives aleemisiaka.new\nAwesome\nBack to mv command\n1 mv \u0026#34;$FILENAME}\u0026#34; \u0026#34;${FILENAME.old}.new\u0026#34; Renames the file from aleemisiaka.old to aleemisiaka.new.\nTo replace all files in a directory use the Bash for loop\n1 2 3 4 5 6 7 for f in *.old # Loops through all the files with the .old extension do mv \u0026#34;$f\u0026#34; \u0026#34;${f%.old}.new\u0026#34; # renames from file.old to file.new done # a oneliner for f in *.old; do mv \u0026#34;$f\u0026#34; \u0026#34;${f%.old}.new\u0026#34;; done Au revoir\n","permalink":"https://limistah.dev/posts/change-extension-of-files-in-a-directory/","summary":"\u003ch4 id=\"a-onliner\"\u003eA onliner\u003c/h4\u003e\n\u003cblockquote\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-0-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-1\"\u003e1\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003efor\u003c/span\u003e f in *.old\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e mv \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$f\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"si\"\u003e${\u003c/span\u003e\u003cspan class=\"nv\"\u003ef\u003c/span\u003e\u003cspan class=\"p\"\u003e%.old\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e.new\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e  \u003cspan class=\"k\"\u003edone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003c/blockquote\u003e\n\u003ch3 id=\"how\"\u003eHow???\u003c/h3\u003e\n\u003cp\u003eTo change the name of a file on Linux/Unix use the \u003ccode\u003emv\u003c/code\u003e command\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-1-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-1\"\u003e1\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003emv currentfilename newfilename\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003c/blockquote\u003e\n\u003cp\u003eIf there is a file named \u003ccode\u003ealeemisiaka.old\u003c/code\u003e and want to rename to \u003ccode\u003ealeemisiaka.new\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003eWe can store the filename to a variable with \u003ccode\u003eexport FILENAME=aleemisiaka\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003eAnd use the variable name in the mv command\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-2-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-1\"\u003e1\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003emv \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$FILENAME\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e$ \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"nv\"\u003e$FILENAME\u003c/span\u003e\u003cspan class=\"s2\"\u003e.new\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003c/blockquote\u003e\n\u003cp\u003eThis renames the file from aleem-isiaka.old to \u003ccode\u003ealeemisiaka.old.new\u003c/code\u003e\u003c/p\u003e","title":"Change files extension in a directory"},{"content":"Going through Chapter 2 of CLRS, I was introduced to the concept of divide and conquer, which is a very interesting algorithm technique, and a popular example of the divide and conquer algorithm is merge sort.\nMerge sort has an Θ(nlgn) run time, which is very good for large inputs. When the input is sufficiently small enough, the algorithm has a worse run time compared to a sorting algorithm that completes in Θ(n^2) time. One of the exercises is to create an optimized version of the merge sort that runs the original merge sort algorithm for sufficiently larger inputs where it shines and to run insertion sort [ Θ(n^2) ] when the input size is sufficiently small enough. I have a solution for it on my Clio website, this post walks through the process for the solution.\n","permalink":"https://limistah.dev/posts/optimizing-merge-sort/","summary":"\u003cp\u003eGoing through Chapter 2 of \u003ca \n  href=\"https://clio.limistah.dev/introduction-to-algorithms\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eCLRS\u003c/a\u003e, I was introduced to the concept of divide and conquer, which is a very interesting algorithm technique, and a popular example of the divide and conquer algorithm is merge sort.\u003c/p\u003e\n\u003cp\u003eMerge sort has an Θ(nlgn) run time, which is very good for large inputs. When the input is sufficiently small enough, the algorithm has a worse run time compared to a sorting algorithm that completes in Θ(n^2) time. One of the exercises is to create an optimized version of the merge sort that runs the original merge sort algorithm for sufficiently larger inputs where it shines and to run insertion sort [ Θ(n^2) ] when the input size is sufficiently small enough. I have a solution for it on my Clio website, this post walks through the process for the solution.\u003c/p\u003e","title":"Optimizing Merge Sort"},{"content":"To search through the man pages for some keywords, use the -k option.\nman -k [keyword]\nThis shows a result of the commands, and routines that match the keyword with a one-line description of what they are about.\nman -k passwd shows all the possible entries for passwd in the manual pages.\nThe apropos The man -k [keyword] command is similar to a help utility called apropos which is available both on Unix and Linux. See it as a shortcut.\n","permalink":"https://limistah.dev/posts/apropos-mandb-linux/","summary":"\u003cp\u003eTo search through the man pages for some keywords, use the -k option.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eman -k [keyword]\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003eThis shows a result of the commands, and routines that match the keyword with a one-line description of what they are about.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eman -k passwd\u003c/code\u003e shows all the possible entries for \u003ccode\u003epasswd\u003c/code\u003e in the manual pages.\u003c/p\u003e\n\u003ch4 id=\"the-apropos\"\u003eThe apropos\u003c/h4\u003e\n\u003cp\u003eThe man -k [keyword] command is similar to a help utility called apropos which is available both on Unix and Linux. See it as a shortcut.\u003c/p\u003e","title":"Searching for a pattern in the man pages"},{"content":"There are many ways to get help as a Linux administrator, manual pages are one of them as they are always close - accessible via the terminal.\nThe manual pages, called \u0026ldquo;man pages\u0026rdquo; is a local documentation and description of software packages, drivers, routines, and libraries on a Linux machine.\nTo use it run man [command|library|routine|driver] and replace the command with the name of a command to find a manual.\nAn example man whereis shows the manual pages for the whereis command\nMan Pages exist in sections and are supported by both FreeBSD and Linux.\nBelow is a list of supported sections:\nSection Description 1 User-level commands and applications 2 System calls and kernel error codes 3 Library calls 4 Device drivers and network protocols 5 Standard file formats 6 Games and demonstrations 7 Miscellaneous files and configurations 8 Obscure kernel specifications and interfaces To get help for a keyword at a section run\nman [section] keyword\nTo know about the init kernel call, man 8 init\nCiao\n","permalink":"https://limistah.dev/posts/understanding-man-pages/","summary":"\u003cp\u003eThere are many ways to get help as a Linux administrator, manual pages are one of them as they are always close - accessible via the terminal.\u003c/p\u003e\n\u003cp\u003eThe manual pages, called \u0026ldquo;man pages\u0026rdquo; is a local documentation and description of software packages, drivers, routines, and libraries on a Linux machine.\u003c/p\u003e\n\u003cp\u003eTo use it run \u003ccode\u003eman [command|library|routine|driver]\u003c/code\u003e and replace the command with the name of a command to find a manual.\u003c/p\u003e","title":"Know the man(nual) pages"},{"content":"Communication between service workers and the clients browser window can be achieved by simply doing:\n1 2 3 self.clients.matchAll().then((clients) =\u0026gt; { clients.forEach((client) =\u0026gt; client.postMessage({ msg: \u0026#34;Hello from SW\u0026#34; })) }) The variable self is a reserved keyword in a service worker context. It references the global scope of the current worker execution scope and has some useful properties. It is like the window object of a JavaScript browser context.\nIn the above snippet, all the clients that run the service worker are loaded, then the .postMessage is called to send message directly to the original javascript runtime of the service worker.\nThe limitation Sometimes, the clients.matchAll method will return an empty list, meaning that there are no clients for the current service worker, which is actually not true!\n1 2 3 self.clients.matchAll().then((clients) =\u0026gt; { console.log(clients) // [] -\u0026gt; No client, which is not true }) Or event using a waitUntil on an event object:\n1 2 3 4 5 6 7 8 9 10 11 12 const messaging = firebase.messaging() self.onmessage = (event) =\u0026gt; { event.waitUntil( clients .matchAll({ type: \u0026#34;window\u0026#34;, }) .then(function (clientList) { console.log(clientList) // [] }) ) } And without the client object, it is impossible to send message to a browser JavaScript context.\nA possible solution We can use the BroadcastChannel API to send messages to and from a service worker context from a JavaScript context.\nThe BroadcastChannel API serves like an event Bus inside of a browser. It registers a channel that lives through out the entire lifecycle of the JavaScript runtime, and the channel would be able to send and receive messages regardless of where it is initiated or called.\nBroadcast Channel has a one to many subscription model, there can as many subscribers listening to events from a single channel. Also, the events are only sent and received to scripts executing on same origin, even in different browser tab or browser window.\nUsing BroadcastChannel To create an broadcast channel, initiate the channel then pass the name of the channel for our event broadcasts.\n1 const broadCaster = new BroadcastChannel(\u0026#34;sw-messages\u0026#34;) Now, the channel can be used to send a message:\n1 broadCaster.postMessage({ message: \u0026#34;Hello from BroadcastChannel\u0026#34; }) The broadcasted message can be consumed with:\n1 2 3 4 const receiver = new BroadcastChannel(\u0026#34;sw-messages\u0026#34;) receiver.addEventListener(\u0026#34;message\u0026#34;, function eventListener(event) { console.log(event) }) Using the addEvenListener to listen to the \u0026ldquo;message\u0026rdquo; event, any message broadcasted to the sw-messages channel, would be handled by the code above.\nUse cases BroadcastChannel is not limited to service workers. It is mostly useful when two different parts of an application are running at different contexts but need to pass information across to each other.\nScenarios like, keeping track of changes within a web app running in different tabs, like the logout button click. Also, keeping track of user interactions/updates from a remote server on a web app running in different tabs.\nBrowser compatibility? This feature is part of web standard but still not supported in Safari but has good support in Chrome and Firefox.\nCiao!\n","permalink":"https://limistah.dev/posts/send-message-from-service-worker-broadcastchannel/","summary":"\u003cp\u003eCommunication between service workers and the clients browser window can be achieved by simply doing:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-0-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-1\"\u003e1\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-2\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-2\"\u003e2\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-3\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-3\"\u003e3\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-js\" data-lang=\"js\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eclients\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ematchAll\u003c/span\u003e\u003cspan class=\"p\"\u003e().\u003c/span\u003e\u003cspan class=\"nx\"\u003ethen\u003c/span\u003e\u003cspan class=\"p\"\u003e((\u003c/span\u003e\u003cspan class=\"nx\"\u003eclients\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nx\"\u003eclients\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eforEach\u003c/span\u003e\u003cspan class=\"p\"\u003e((\u003c/span\u003e\u003cspan class=\"nx\"\u003eclient\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"nx\"\u003eclient\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003epostMessage\u003c/span\u003e\u003cspan class=\"p\"\u003e({\u003c/span\u003e \u003cspan class=\"nx\"\u003emsg\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Hello from SW\u0026#34;\u003c/span\u003e \u003cspan class=\"p\"\u003e}))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e})\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cblockquote\u003e\n\u003cblockquote\u003e\n\u003cblockquote\u003e\n\u003cp\u003eThe variable \u003ccode\u003eself\u003c/code\u003e is a reserved keyword in a service worker context. It references the global scope of the current worker execution scope and has some useful properties. It is like the window object of a JavaScript browser context.\u003c/p\u003e\u003c/blockquote\u003e\u003c/blockquote\u003e\u003c/blockquote\u003e\n\u003cp\u003eIn the above snippet, all the clients that run the service worker are loaded, then the \u003ccode\u003e.postMessage\u003c/code\u003e is called to send message directly to the original javascript runtime of the service worker.\u003c/p\u003e","title":"Send message from a service worker"},{"content":"First, install CompileDaemon:\n1 $ go get github.com/githubnemo/CompileDaemon \u0026amp;\u0026amp; go install github.com/githubnemo/CompileDaemon Then, from the root of the project, create a Make file:\n1 $ touch Makefile And add the below content:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 GOCMD ?= go GOBUILD = $(GOCMD) build GOCLEAN = $(GOCMD) clean GOTEST = $(GOCMD) test GOGET = $(GOCMD) get BINARY_NAME = project_name BINARY_UNIX = $(BINARY_NAME)_unix default: all all: test build build: $(GOBUILD) -o ../$(BINARY_NAME) -v -ldflags=\u0026#34;-X main.VERSION=$(TAG)\u0026#34; test: $(GOTEST) -v ./... clean: $(GOCLEAN) rm -f $(BINARY_NAME) rm -f $(BINARY_UNIX) run: build ./$(BINARY_NAME) dev: CompileDaemon -build=\u0026#34;$(GOBUILD) -o ../$(BINARY_NAME)\u0026#34; -command=\u0026#34;../$(BINARY_NAME)\u0026#34; -color=\u0026#34;true\u0026#34; -exclude-dir=.git -exclude=\u0026#34;.#*\u0026#34; Finally, from the root of your project:\n1 $ make dev Voila!\n","permalink":"https://limistah.dev/posts/autocompile-go/","summary":"\u003cp\u003eFirst, install CompileDaemon:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-0-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-1\"\u003e1\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e$ go get github.com/githubnemo/CompileDaemon \u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e go install github.com/githubnemo/CompileDaemon\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003eThen, from the root of the project, create a Make file:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-1-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-1\"\u003e1\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e$ touch Makefile\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003eAnd add the below content:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-2-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-1\"\u003e 1\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-2\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-2\"\u003e 2\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-3\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-3\"\u003e 3\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-4\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-4\"\u003e 4\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-5\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-5\"\u003e 5\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-6\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-6\"\u003e 6\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-7\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-7\"\u003e 7\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-8\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-8\"\u003e 8\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-9\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-9\"\u003e 9\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-10\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-10\"\u003e10\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-11\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-11\"\u003e11\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-12\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-12\"\u003e12\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-13\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-13\"\u003e13\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-14\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-14\"\u003e14\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-15\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-15\"\u003e15\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-16\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-16\"\u003e16\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-17\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-17\"\u003e17\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-18\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-18\"\u003e18\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-19\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-19\"\u003e19\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-20\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-20\"\u003e20\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-21\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-21\"\u003e21\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-22\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-22\"\u003e22\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-23\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-23\"\u003e23\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-24\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-24\"\u003e24\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-25\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-25\"\u003e25\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-26\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-26\"\u003e26\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-27\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-27\"\u003e27\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-makefile\" data-lang=\"makefile\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003eGOCMD\u003c/span\u003e \u003cspan class=\"o\"\u003e?=\u003c/span\u003e go\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003eGOBUILD\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003e$(\u003c/span\u003eGOCMD\u003cspan class=\"k\"\u003e)\u003c/span\u003e build\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003eGOCLEAN\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003e$(\u003c/span\u003eGOCMD\u003cspan class=\"k\"\u003e)\u003c/span\u003e clean\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003eGOTEST\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003e$(\u003c/span\u003eGOCMD\u003cspan class=\"k\"\u003e)\u003c/span\u003e \u003cspan class=\"nb\"\u003etest\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003eGOGET\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003e$(\u003c/span\u003eGOCMD\u003cspan class=\"k\"\u003e)\u003c/span\u003e get\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003eBINARY_NAME\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e project_name\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003eBINARY_UNIX\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003e$(\u003c/span\u003eBINARY_NAME\u003cspan class=\"k\"\u003e)\u003c/span\u003e_unix\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nf\"\u003edefault\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eall\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nf\"\u003eall\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003etest\u003c/span\u003e \u003cspan class=\"n\"\u003ebuild\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nf\"\u003ebuild\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\t\u003cspan class=\"k\"\u003e$(\u003c/span\u003eGOBUILD\u003cspan class=\"k\"\u003e)\u003c/span\u003e -o ../\u003cspan class=\"k\"\u003e$(\u003c/span\u003eBINARY_NAME\u003cspan class=\"k\"\u003e)\u003c/span\u003e -v -ldflags\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;-X main.VERSION=\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003eTAG\u003cspan class=\"k\"\u003e)\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nf\"\u003etest\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\t\u003cspan class=\"k\"\u003e$(\u003c/span\u003eGOTEST\u003cspan class=\"k\"\u003e)\u003c/span\u003e -v ./...\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nf\"\u003eclean\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\t\u003cspan class=\"k\"\u003e$(\u003c/span\u003eGOCLEAN\u003cspan class=\"k\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\trm -f \u003cspan class=\"k\"\u003e$(\u003c/span\u003eBINARY_NAME\u003cspan class=\"k\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\trm -f \u003cspan class=\"k\"\u003e$(\u003c/span\u003eBINARY_UNIX\u003cspan class=\"k\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nf\"\u003erun\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003ebuild\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\t./\u003cspan class=\"k\"\u003e$(\u003c/span\u003eBINARY_NAME\u003cspan class=\"k\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nf\"\u003edev\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\tCompileDaemon -build\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003eGOBUILD\u003cspan class=\"k\"\u003e)\u003c/span\u003e\u003cspan class=\"s2\"\u003e -o ../\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003eBINARY_NAME\u003cspan class=\"k\"\u003e)\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e -command\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;../\u003c/span\u003e\u003cspan class=\"k\"\u003e$(\u003c/span\u003eBINARY_NAME\u003cspan class=\"k\"\u003e)\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e -color\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;true\u0026#34;\u003c/span\u003e -exclude-dir\u003cspan class=\"o\"\u003e=\u003c/span\u003e.git -exclude\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;.#*\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003eFinally, from the root of your project:\u003c/p\u003e","title":"Autocompile Go"},{"content":"Hi there,\nOne of the unique features of humans is the exceptional way we interact with each other.\nSharing how we feel and what we are thinking helps others better align with us.\nI am writing on Substack Yeah, I needed a place that holds my progress over time and somewhere I can share relevant information with people in the tech space. I remember that my journey started with having selftaughco.de, which is dead by now, to me.aleemisiaka.com, to aleemisiaka.com, and now limistah.dev\nThese transitions have followed a pattern of me sharing my thoughts about life and living, as well as my work.\nThis section is a way for me to reach a target audience (my friends, perhaps) and relate with them, since I keep a small circle of friends; others are welcome, too.\nWhat would you get? Primarily, this section will discuss life itself, a publicly moderated and edited personal journal.\nI am sure you are interested!\nYeah…\nSo, look out for my next posts on this section!\nCheers\nSalut!\n","permalink":"https://limistah.dev/essays/0002-what-to-expect/","summary":"\u003cp\u003eHi there,\u003c/p\u003e\n\u003cp\u003eOne of the unique features of humans is the exceptional way we interact with each other.\u003c/p\u003e\n\u003cp\u003eSharing how we feel and what we are thinking helps others better align with us.\u003c/p\u003e\n\u003ch2 id=\"i-am-writing-on-substack\"\u003eI am writing on Substack\u003c/h2\u003e\n\u003cp\u003eYeah, I needed a place that holds my progress over time and somewhere I can share relevant information with people in the tech space. I remember that my journey started with having selftaughco.de, which is dead by now, to me.aleemisiaka.com, to aleemisiaka.com, and now limistah.dev\u003c/p\u003e","title":"What to expect"},{"content":"When programming Erlang, you should think like you are writing an English essay. In Erlang, functions are not very different to what a traditional programming language offers, but they are written very differently in Erlang.\nTo declare a function in the Erlang Repl, you will have to use the fun keyword.\n1 Name = fun(X) -\u0026gt; X. The above code will store the the declaration of a function called Name and would receive an argument called X.\nAs you can see, there is no return keyword to specify the return value. In Erlang everything is an expression and must have a value, to ease working with function, the last expression of the function becomes the value of the function, hence X here is the value of the anonymous function that we defined.\nConsider this basic function declaration in an Erlang Repl\n1 Rectangle_Area = fun({Width, Height}) -\u0026gt; Width * Height end. This is very easy to read right. Building up on the pattern matching lesson, it is clear that the function declaration is doing pattern matching to identify its arguments.\nIf you think like I do, you will see that we can overload function with different arguments. Like this:\n1 2 3 4 Price = fun({car}) -\u0026gt; 20; ({bottle}) -\u0026gt; 1; ({fish}) -\u0026gt; .5 end. It should be noticed that we have defined the same function with different arguments. Erlang understands that when we try to call this function, it should do a pattern match against the argument to determine which declaration it should run.\nHence, if Price is called with {car}, it would run the code for Price = fun({car}) -\u0026gt; 20.\nOne subtleness to notice is that when you are doing function overloading, you should separate function definitions with semicolon ; and end the last function with end..\nHigh Order Function is a unique feature of functional programming languages, Erlang ships with this great feature. Erlang functions can be passed as argument, and functions can return another function.\n1 2 3 4 5 Map = fun([H|T],F) -\u0026gt; [ F(H) | map(F,T) ], ([], _) -\u0026gt; [] end. %% 2,4,6,8,10,12,14,16,18 Map([1,2,3,4,5,6,7,8,9], fun(X) -\u0026gt; 2 * X end). You should not worry your self with some of the syntax here, we will discuss them very shortly. What you should be concerned with is how we have been able to pass functions to another function call.\nBIFs This is a short acronym for Build In Functions. Erlang ships with a handful of useful built in functions. Check here for a complete list of the BIFs shipped in every erlang installation.\nNow that you have this basic understanding of functions, move up the ladder to know about modules.\n","permalink":"https://limistah.dev/posts/erlang-functions/","summary":"\u003cp\u003eWhen programming Erlang, you should think like you are writing an English essay. In Erlang, functions are not very different to what a traditional programming language offers, but they are written very differently in Erlang.\u003c/p\u003e\n\u003cp\u003eTo declare a function in the Erlang Repl, you will have to use the \u003ccode\u003efun\u003c/code\u003e keyword.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-0-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-1\"\u003e1\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-erlang\" data-lang=\"erlang\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003eName\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003efun\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nv\"\u003eX\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"nv\"\u003eX\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003eThe above code will store the the declaration of a function called \u003ccode\u003eName\u003c/code\u003e and would receive an argument called X.\u003c/p\u003e","title":"Erlang Functions"},{"content":"If you come from a conventional programming language background, the way Erlang handles assignment is expected to look wonky, but it is not.\nThere is nothing like an assignment in Erlang programming language; there is a different approach to accessing values in memory, which is the pattern-matching operations.\nWith Java, PHP, Python, C, C++, and likes, the = symbol implies take the values from the right, and store it into the memory, then give me the reference of the location in memory and store it in the expression at the left.\nSuch that const name = \u0026quot;Aleem Isiaka\u0026quot; is correct in JavaScript, similar constructs exist in other languages using this concept.\nIn Erlang, X = 20 reads very differently.\nThe Erlang runtime Beam understands it as Evaluate the expressions at the right, match it with the value of the expressions at the left.\nClarifying this further, the runtime first evaluates 20, which is a valid Erlang term (integer), then goes to the left to check if the X has a value already; in our case, it does not, meaning the X variable is unbound, an unbound variable will match any value at the right of the match operator (=).\nThis is what happens when we do X = 20. Did you get it?\nThe above example might seem off, let\u0026rsquo;s take this a little bit further. Consider the below Erlang snippet:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 X = 20. Y = 30. Z = 30. X = Y. %% -\u0026gt; Fails, 20 is not equals 30 Y = X. %% -\u0026gt; Fails, 30 is not equals 20 Y = 30. %% -\u0026gt; Works, 30(Y) = 30 30 = Y. %% -\u0026gt; Works, 30 = Y(30) Z = Y. %% Works, 30 = 30 Y = Z. %% Works 30 = 30 %% Works? someFuncThatReturns30 = func() -\u0026gt; 30. Y = someFuncThatReturns30(). %% -\u0026gt; Sure, it does, as long as it returns a value that Y has been bounded to before now or Y is an unbound variable First, we have X matched(bounded, nothing like initialization in Erlang) to 20, with Y and Z bounded to 30. What we should note here is that:\nMatching bounded variables with different values would throw an exception, like where X = Y or Y = X fails the match operation since 20 is not equal 30.\nMatching bounded variables with hardcoded Erlang terms like atoms, binaries integers would match as long as both sides have the same value. This is why an initialized Y is equal to an equivalent of its bounded value 20 Matching variables bounded differently would succeed as long as both variables contain the same value. The return value of a function (func in Erlang) would be successful in matching a variable that holds the same value as the returned value or an unbound variable. For example, an already bounded Y would always check the return value of someFuncThatReturns30 as they are both equal. Yes, that is all to Erlang pattern matching, but you might be wondering, how can I build a robust system if I can\u0026rsquo;t match a variable to another value that it is bounded to? Or do I can run out of names if I keep needing variables. You are not alone; I was here too!\nThe simple answer to this question is that: Erlang is a modular programming language and expects that you write your programs in modules. And modules are a way to group functions in a program.\nThus, in Erlang, variables have lexical scoping and only live in the context/function/process they are declared.\nThis does not make an Erlang program only scalable, and it also makes it more predictable as you don\u0026rsquo;t expect a variable to have two different values in the same context.\nVariable names can be duplicated in different contexts:\ns a o n m o e t F x y h a h u e l a n r l l c F f u = 2 3 n 0 0 ( ; . = 1 X 0 5 ) ( 0 0 X ; . ) someFunc takes in one argument, which is pattern matched against x or y, both of which are atoms to return the requested value. While anotherFunc takes in one argument and pattern matching against all or half to return the requested value.\nWe can notice that both have X as the name of their argument; this is a valid Erlang module code that would be successful if it runs.\nThe operation\u0026rsquo;s success illustrates that we have to be careful of the context we are in not to bind a variable to an already bounded variable and pattern match correctly.\nWith these, I believe you have the basics of Erlang Pattern Matching.\nPattern matching is an excellent tool in writing programs for Erlang and other languages that depend on its VM.\nNow that you have its basic understanding move up the ladder to know about functions.\nNamaste!\n","permalink":"https://limistah.dev/posts/erlang-pattern-matching/","summary":"\u003cp\u003eIf you come from a conventional programming language background, the way Erlang handles \u003cem\u003eassignment\u003c/em\u003e is expected to look \u003cstrong\u003ewonky\u003c/strong\u003e, but it is not.\u003c/p\u003e\n\u003cp\u003eThere is nothing like an assignment in Erlang programming language; there is a different approach to accessing values in memory, which is the pattern-matching operations.\u003c/p\u003e\n\u003cp\u003eWith Java, PHP, Python, C, C++, and likes, the \u003ccode\u003e=\u003c/code\u003e symbol implies \u003cem\u003etake the values from the right, and store it into the memory, then give me the reference of the location in memory and store it in the expression at the left\u003c/em\u003e.\u003c/p\u003e","title":"Erlang Pattern Matching"},{"content":"If you have worked with other languages like JavaScript, Java, Python etc, you would be surprised by what Erlang understands as variable.\nIn Erlang, variables starts with uppercase letter, thus, C, X, Ape, Ant are all valid identifiers for Erlang variables.\nVariables can not start with lowercase letter or begin with a number.\nErlang variables can include can alphanumeric characters, an underscore and @ symbol.\n1 2 3 X = 1. %% Valid y = 2. %% Invalid 1X = 2. %% Invalid When assigning to a variable - as we do call it in other languages, Erlang actually does something called pattern matching. Comparing the values on the right to the values on the left.\nA pattern match would only succeed if the two operands match.\nThus, in Erlang, a variable get assigned to if it is either unbound or has the same value as the value at the right.\nThe = is a special symbol that does not do assignment but makes the pattern matching operation to be successful if its conditions are met.\nHence, in Erlang = is known as a pattern match operator which evaluates the value of the right hand side (RHS) then matching the result with the left hand side (LHS)\n1 2 3 4 5 6 7 8 X = 1. %% successful Y = 2. %% successful Z = X + 1. %% successful, Z is unbound to Z. %% =\u0026gt; 3 Z = 3 %% successful, Z is now 3, but, we want it to have the value 3 Z = 4 %% fails, Z is already holding 3, and 4 won\u0026#39;t match against it. There is nothing as global or private scope in Erlang, all variables are lexically scoped and are unrelated even if they exists in different functions;\nContinue to the next post on Erlang Learning: Erlang Pattern Matching\nCiao!\n","permalink":"https://limistah.dev/posts/erlang-variables/","summary":"\u003cp\u003eIf you have worked with other languages like JavaScript, Java, Python etc, you would be surprised by what Erlang understands as variable.\u003c/p\u003e\n\u003cp\u003eIn Erlang, variables starts with uppercase letter, thus, \u003ccode\u003eC\u003c/code\u003e, \u003ccode\u003eX\u003c/code\u003e, \u003ccode\u003eApe\u003c/code\u003e, \u003ccode\u003eAnt\u003c/code\u003e are all valid identifiers for Erlang variables.\u003c/p\u003e\n\u003cp\u003eVariables can not start with lowercase letter or begin with a number.\u003c/p\u003e\n\u003cp\u003eErlang variables can include can alphanumeric characters, an underscore and @ symbol.\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-0-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-1\"\u003e1\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-2\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-2\"\u003e2\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-3\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-3\"\u003e3\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-erlang\" data-lang=\"erlang\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nv\"\u003eX\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e \u003cspan class=\"c\"\u003e%% Valid\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e\u003c/span\u003e\u003cspan class=\"n\"\u003ey\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e \u003cspan class=\"c\"\u003e%% Invalid\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c\"\u003e\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"nv\"\u003eX\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e \u003cspan class=\"c\"\u003e%% Invalid\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003eWhen \u003cem\u003eassigning\u003c/em\u003e to a variable - as we do call it in other languages, Erlang actually does something called \u003cem\u003epattern matching\u003c/em\u003e. Comparing the values on the right to the values on the left.\u003c/p\u003e","title":"Erlang Variables"},{"content":"Without JavaScript, dynamic UI is possible with just CSS action class selectors.\nCase study Help text for input element which is only visible when the input is focused.\nThe HTML 1 2 3 4 \u0026lt;div class=\u0026#34;input-cont\u0026#34;\u0026gt; \u0026lt;input type=\u0026#34;text\u0026#34; placeholder=\u0026#34;Focus me\u0026#34; autofocus /\u0026gt; \u0026lt;span class=\u0026#34;help\u0026#34; data-help=\u0026#34;Enter your email\u0026#34;\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; To achieve the above, a span holding the help text in a data-help attribute as a sibling to the actual input. Both the input and the span are children to a parent div with class name input-cont.\nThe interaction(CSS) The container would help to proper place the content, so a position relative is applied. Since multiple inputs can exist on a page, a margin-bottom is added to give a visual cue and separation.\n1 2 3 4 5 6 7 8 .input-cont { position: relative; margin-bottom: 1rem; } input { z-index: -1; } The Structure Instead of additional element to hold the text, the ::before pseudo element is already available for container elements.\n\u0026lt;input /\u0026gt; elements are not container which makes it difficult for this purpose, a span is placed next to the input element to provide the flexibility that ::before, ::after pseudo elements provide.\nThe +(direct sibling) selector can achieve selecting an element that lives under the same parent as the base selector. Since the input and the span.help lives under the same parent, so input+.help gets the span.help element, and the ::before gives access to its pseudo selector.\nTo ensure that the ::before pseudo element stay behind the input field, the z-index: -2 is applied to it, also, the content property is not set at the initial state of the element, making the element hidden by default.\n1 2 3 4 5 6 7 8 9 10 11 input + .help::before { bottom: 50%; left: 0; width: 100%; font-size: 0px; color: #707070; opacity: 0; position: absolute; filter: blur(5px); z-index: -2; } Toggling the help text To show the help text when an element is hidden, the :focus psuedo selector is used to select an input that is focused, then the sibling selecor gives access to the span.help element, which makes the span.help::before pseudo element stylable.\nHere, the content is set by picking it from the data-help attribute on the span.help element, a final z-index: 0 is applied to bring the span.help::before element on top of the input field, while the bottom: 60% pushes the element downward below the input field to avoid overlapping.\n1 2 3 4 5 6 7 8 input:focus + .help::before { content: attr(data-help); opacity: 1; filter: blur(0); bottom: -60%; z-index: 0; font-size: 12px; } Find the above code on codepen.io\nAnd this is just a basic toggling and hiding of elements using :focus.\nMany interesting things can be built with CSS user action selectors.\nThere are other action selectors are:\n:active :focus :focus-within :focus-visible :hover Salut!\n","permalink":"https://limistah.dev/posts/css-action-pseudo-classes/","summary":"\u003cp\u003eWithout JavaScript, dynamic UI is possible with \u003cstrong\u003ejust\u003c/strong\u003e CSS action class selectors.\u003c/p\u003e\n\u003ch3 id=\"case-study\"\u003eCase study\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003cem\u003eHelp text for input element which is only visible when the input is focused.\u003c/em\u003e\u003c/p\u003e\u003c/blockquote\u003e\n\u003ch2 id=\"the-html\"\u003eThe HTML\u003c/h2\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-0-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-1\"\u003e1\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-2\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-2\"\u003e2\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-3\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-3\"\u003e3\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-4\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-4\"\u003e4\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-html\" data-lang=\"html\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ediv\u003c/span\u003e \u003cspan class=\"na\"\u003eclass\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;input-cont\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003einput\u003c/span\u003e \u003cspan class=\"na\"\u003etype\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;text\u0026#34;\u003c/span\u003e \u003cspan class=\"na\"\u003eplaceholder\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Focus me\u0026#34;\u003c/span\u003e \u003cspan class=\"na\"\u003eautofocus\u003c/span\u003e \u003cspan class=\"p\"\u003e/\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003espan\u003c/span\u003e \u003cspan class=\"na\"\u003eclass\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;help\u0026#34;\u003c/span\u003e \u003cspan class=\"na\"\u003edata-help\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;Enter your email\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003espan\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003ediv\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003eTo achieve the above, a \u003ccode\u003espan\u003c/code\u003e holding the help text in a \u003ccode\u003edata-help\u003c/code\u003e attribute as a sibling to the actual input. Both the input and the span are children to a parent div with class name \u003ccode\u003einput-cont\u003c/code\u003e.\u003c/p\u003e","title":"CSS action pseudo classes"},{"content":"If you have used WhatsApp or Facebook Chat, then you have one way or the other interacted with an Erlang-backed system.\nErlang is a language created for the telecommunication industry by Jor Armstrong, Robert Virding, and Mike Williams in 1986. It was recorded that Jor Armstrong claimed he was provided a library and did not know what to do with it, then they taught him to solve the reliability and concurrent problem of the telecommunication industry, and that gave birth to Erlang.\nThe strength of Erlang lies in running scalable applications in a Distributed environment. It allows computers to network with each other with a very little overhead on the programmer and the operating system.\nIn this post, we will be getting a formal introduction to Erlang and we are just covering the basics. We would be covering variables, funcs (functions), modules, types, records, maps, processes, and distributed systems.\nThe whole of the OTP framework won\u0026rsquo;t be covered. In the future, we will be having a post about this!\nInstallation and Initialization For a thorough guide on how to install Erlang runtime on different platforms, check out Bruce Yinhe\u0026rsquo;s post on Medium about this.\nAfter a successful installation, open a terminal/CMD, and type in erl, a default welcome message and prompt should be seen. Great, welcome to the Erlang world.\nNow that we have covered the basic installation, follow the links below to learn more about Erlang and its syntax.\nVariables Pattern Matching Funcs Modules Types Records Maps Processes Processes communication Distributed system ","permalink":"https://limistah.dev/posts/an-introduction-to-erlang/","summary":"\u003cp\u003eIf you have used WhatsApp or Facebook Chat, then you have one way or the other interacted with an Erlang-backed system.\u003c/p\u003e\n\u003cp\u003eErlang is a language created for the telecommunication industry by \u003ca \n  href=\"https://en.wikipedia.org/wiki/Joe_Armstrong_%28programmer%29\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eJor Armstrong\u003c/a\u003e, \u003ca \n  href=\"\"\n  \u003e\u003cem\u003eRobert Virding,\u003c/em\u003e\u003c/a\u003e and \u003cem\u003eMike Williams\u003c/em\u003e in 1986. It was recorded that \u003ca \n  href=\"https://en.wikipedia.org/wiki/Joe_Armstrong_%28programmer%29\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eJor Armstrong\u003c/a\u003e claimed he was provided a library and did not know what to do with it, then they taught him to solve the reliability and concurrent problem of the telecommunication industry, and that gave birth to Erlang.\u003c/p\u003e","title":"An introduction to Erlang"},{"content":"CSS can keep count of numbers without writing any additional JavaScript.\nIt does this by taking note the amount of time a CSS block affects a page then incrementing the counter for that block if the counter-increment rule is implemented.\nFor example:\n1 2 3 input:invalid { counter-increment: invalid-count; } With no JavaScript at all, CSS understands that whenever there is an invalid element, it should increment the count for the invalid-count identifier.\nStructure of CSS counters CSS counter is a CSS rule that specify the increment-counter property and an indentifier for the counter to increment.\nTo know how many HTML exists in a page:\n1 2 3 h1 { increment-counter: total-h1-counter; } Since CSS would match every h1 on the page, the increment-counter would be executed, thereby increasing the count for the total-h1-counter.\nGetting back to 0 A counter can go back to the 0 state by declaring the counter-reset: counter-identifier CSS rule\nA use case here would be always reseting the number of total-h1-counter on the page when the body of the document is matched, but incrementing when H1s are matched.\nThe CSS below helps to achieve that:\n1 2 3 4 5 6 7 body { counter-reset: total-h1-counter; } h1 { counter-increment: total-h1-counter; } Rendering counter on a page. We can use CSS Generated Content to make the value of a counter visible on the page. We can achieve this using pseudo elements(::before, ::after) through the content property.\n1 2 3 4 5 6 \u0026lt;h1\u0026gt;The first\u0026lt;/h1\u0026gt; \u0026lt;h1\u0026gt;The Second\u0026lt;/h1\u0026gt; \u0026lt;h1\u0026gt;The third\u0026lt;/h1\u0026gt; \u0026lt;h1\u0026gt;The fourhtb\u0026lt;/h1\u0026gt; \u0026lt;section\u0026gt;There are\u0026lt;/section/\u0026gt; 1 2 3 4 5 6 7 8 9 10 11 body { counter-reset: total-h1-counter; } h1 { counter-increment: total-h1-counter; } section:after { content: \u0026#34; \u0026#34; counter(total-h1-counter) \u0026#34; H1 tags on the page\u0026#34;; font-weight: bold; color: tomato; } Salut!\n","permalink":"https://limistah.dev/posts/css-counters/","summary":"\u003cp\u003eCSS can keep count of numbers without writing any additional JavaScript.\u003c/p\u003e\n\u003cp\u003eIt does this by taking note the amount of time a CSS block affects a page then incrementing the counter for that block if the \u003ccode\u003ecounter-increment\u003c/code\u003e rule is implemented.\u003c/p\u003e\n\u003cp\u003eFor example:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-0-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-1\"\u003e1\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-2\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-2\"\u003e2\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-3\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-3\"\u003e3\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003einput\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"nd\"\u003einvalid\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003ecounter-increment\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003einvalid-count\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003eWith no JavaScript at all, CSS understands that whenever there is an invalid element, it should increment the count for the \u003ccode\u003einvalid-count\u003c/code\u003e identifier.\u003c/p\u003e","title":"CSS counters"},{"content":"Yeah, you read that right. Let go straight into it!\nA Test Case Can you interprete this CSS selector query?\n1 2 3 p.title:first-of-type { color: red; } Let me think like you would:\nSelect every P element that has the class name of title and apply the color red to the first of its type.\n1 2 3 4 \u0026lt;body\u0026gt; \u0026lt;p\u0026gt;Lorem Ipsum\u0026lt;/p\u0026gt; \u0026lt;p class=\u0026#34;title\u0026#34;\u0026gt;Paragraph 2 (shows in red color)\u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; You think you’re right.\nAnother test case What happens with the below:\n1 2 3 4 5 6 7 8 9 10 11 \u0026lt;style\u0026gt; p.paragraph-1:first-child { color: green; } \u0026lt;/style\u0026gt; \u0026lt;div\u0026gt; \u0026lt;p class=\u0026#34;paragraph-1\u0026#34;\u0026gt; \u0026lt;span class=\u0026#34;span1\u0026#34;\u0026gt;This is the title\u0026lt;/span\u0026gt; \u0026lt;span class=\u0026#34;span2\u0026#34;\u0026gt; Shouldn\u0026#39;t be the title \u0026lt;/span\u0026gt; \u0026lt;/p\u0026gt; \u0026lt;/div\u0026gt; You would expect the SPAN with the class name span1 should have the color green.\nTo you, the query reads:\nSelect the first child of every P tag that has a class name of paragraph-1,\nWell,that is not correct!\nThe query correctly reads:\nSelect every P tag with a class name of paragraph-1 and IT IS THE FIRST CHILD of its parent.\nKnow the Strcuture To better understand Structural selectors in CSS, we have to consider the query first and then attach the selector\u0026rsquo;s semantic meaning.\n:only-child should read as AND IS THE ONLY CHILD, but not seeing it as going deeper to the actual element\u0026rsquo;s children.\nStructural selectors always refer to the position of an element itself in a document.\n1 2 3 4 \u0026lt;p\u0026gt; \u0026lt;h1\u0026gt;1\u0026lt;/h1\u0026gt; \u0026lt;h1\u0026gt;2\u0026lt;/h1\u0026gt; \u0026lt;/p\u0026gt; For the structure above, h1:first-child would pick the first h1, h1:last-child would select the second h1.\nThe beauty of structural selectors is that page structures are stylable regardless of their complexity.\nFor example, the only child of an element, empty elements, any element that is a child to an element but existing at an index, and more are stylable, which is powerful!\nRef List Reference the list below to understand structural selectors proper:\n\u0026lt;selector\u0026gt;:first-child - Will match any element that is the first child \u0026lt;selector\u0026gt;:last-child - will match any element that is the last child \u0026lt;selector\u0026gt;:only-child - will match any element that is the only child of its element \u0026lt;selector\u0026gt;:nth-child(n) - will match any element that is a child of a parent element but occurring at the index of n for that selector \u0026lt;selector\u0026gt;:nth-last-child(n) - will match any element that is a child element occurring at the index of n for that selector. Take it as `nth-child moving in reverse mode. \u0026lt;selector\u0026gt;:empty - will match any element that is empty or has just comments \u0026lt;selector\u0026gt;:first-of-type - will match any element, and it is the first occurrence for that selector \u0026lt;selector\u0026gt;:last-of-type - will match any element that is the last occurrence for that selector \u0026lt;selector\u0026gt;:nth-of-type(n) - will match any element occurring at index of n for that selector \u0026lt;selector\u0026gt;:nth-last-of-type(n) - will match the last element for every element occurring at the index of n for that selector. Take it as nth-type moving in reverse mode. \u0026lt;selector\u0026gt;:only-of-type - will match the only element that has for that selector. Interesting right? Play with it here.\nTake home Structural selectors apply themselves to the selected element to help us style an element existing at a specific position in the HTML.\nau revoir!\n","permalink":"https://limistah.dev/posts/structural-css-selectors/","summary":"\u003cp\u003eYeah, you read that right. Let go straight into it!\u003c/p\u003e\n\u003ch2 id=\"a-test-case\"\u003eA Test Case\u003c/h2\u003e\n\u003cp\u003eCan you interprete this CSS selector query?\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-0-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-1\"\u003e1\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-2\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-2\"\u003e2\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-3\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-3\"\u003e3\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-css\" data-lang=\"css\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003ep\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nc\"\u003etitle\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"nd\"\u003efirst-of-type\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"k\"\u003ecolor\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003ered\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003eLet me think like you would:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eSelect every \u003ccode\u003eP\u003c/code\u003e element that has the class name of title and apply the color red to the first of its type.\u003c/p\u003e\u003c/blockquote\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-1-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-1\"\u003e1\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-2\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-2\"\u003e2\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-3\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-3\"\u003e3\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-1-4\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-4\"\u003e4\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-html\" data-lang=\"html\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ebody\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ep\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003eLorem Ipsum\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003ep\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003ep\u003c/span\u003e \u003cspan class=\"na\"\u003eclass\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\u0026#34;title\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003eParagraph 2 (shows in red color)\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003ep\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nt\"\u003ebody\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003eYou think you’re right.\u003c/p\u003e","title":"Structural CSS Selectors"},{"content":"Welcome to my Cappings.\nIn the beginning was the Word, and the Word was with God, and the Word was God.\nI live to finally say: All praises be to the Lord, Lord of the world!\nSign up now so you don\u0026rsquo;t miss the first issue.\nIn the meantime, tell your friends by clicking the icons below!\n","permalink":"https://limistah.dev/essays/0001-coming-soon/","summary":"\u003cp\u003eWelcome to my Cappings.\u003c/p\u003e\n\u003cp\u003eIn the beginning was the Word, and the Word was with God, and the Word was God.\u003c/p\u003e\n\u003cp\u003eI live to finally say: All praises be to the Lord, Lord of the world!\u003c/p\u003e\n\u003cp\u003eSign up now so you don\u0026rsquo;t miss the first issue.\u003c/p\u003e\n\u003cp\u003eIn the meantime, tell your friends by clicking the icons below!\u003c/p\u003e","title":"Building and Shipping Useful Stuffs."},{"content":" Open ios/{APP_NAME}/Info.plist.\nAdd\n1 2 3 4 5 6 7 8 9 10 11 \u0026lt;plist\u0026gt; \u0026lt;dict\u0026gt; ... ... ... \u0026lt;key\u0026gt;UIBackgroundModes\u0026lt;/key\u0026gt; \u0026lt;array\u0026gt; \u0026lt;string\u0026gt;audio\u0026lt;/string\u0026gt; \u0026lt;/array\u0026gt; \u0026lt;/dict\u0026gt; \u0026lt;/plist\u0026gt; Press r on the metro terminal Voila!\n","permalink":"https://limistah.dev/posts/enable-background-audio-react-native/","summary":"\u003col\u003e\n\u003cli\u003e\n\u003cp\u003eOpen \u003ccode\u003eios/{APP_NAME}/Info.plist\u003c/code\u003e.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eAdd\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-0-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-1\"\u003e 1\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-2\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-2\"\u003e 2\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-3\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-3\"\u003e 3\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-4\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-4\"\u003e 4\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-5\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-5\"\u003e 5\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-6\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-6\"\u003e 6\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-7\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-7\"\u003e 7\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-8\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-8\"\u003e 8\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-9\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-9\"\u003e 9\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-10\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-10\"\u003e10\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-11\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-11\"\u003e11\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-xml\" data-lang=\"xml\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003e\u0026lt;plist\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003e\u0026lt;dict\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  ...\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  ...\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  ...\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e  \u003cspan class=\"nt\"\u003e\u0026lt;key\u0026gt;\u003c/span\u003eUIBackgroundModes\u003cspan class=\"nt\"\u003e\u0026lt;/key\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\t\u003cspan class=\"nt\"\u003e\u0026lt;array\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\t\t\u003cspan class=\"nt\"\u003e\u0026lt;string\u0026gt;\u003c/span\u003eaudio\u003cspan class=\"nt\"\u003e\u0026lt;/string\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\t\u003cspan class=\"nt\"\u003e\u0026lt;/array\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003e\u0026lt;/dict\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nt\"\u003e\u0026lt;/plist\u0026gt;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003col start=\"3\"\u003e\n\u003cli\u003ePress \u003ccode\u003er\u003c/code\u003e on the metro terminal\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e\u003cem\u003eVoila!\u003c/em\u003e\u003c/p\u003e","title":"How to enable background Audio Play in iOS React Native"},{"content":" npm install rollup-plugin-node-resolve rollup-plugin-json.\nAdd it to the plugins inside rollup.config.js\n1 2 3 4 5 6 7 8 9 10 11 // Rollup configuration ... plugins: [ rollupNodeResolve({ jsnext: true, preferBuiltins: true, browser: true }), rollupJson(), ... ] ** Notice the browser: true **\nRun your build again: yarn run build Voila!\nSource\n","permalink":"https://limistah.dev/posts/configure-axios-rollup/","summary":"\u003col\u003e\n\u003cli\u003e\n\u003cp\u003e\u003ccode\u003enpm install rollup-plugin-node-resolve rollup-plugin-json\u003c/code\u003e.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003eAdd it to the plugins inside \u003ccode\u003erollup.config.js\u003c/code\u003e\u003c/p\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-0-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-1\"\u003e 1\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-2\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-2\"\u003e 2\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-3\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-3\"\u003e 3\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-4\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-4\"\u003e 4\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-5\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-5\"\u003e 5\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-6\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-6\"\u003e 6\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-7\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-7\"\u003e 7\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-8\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-8\"\u003e 8\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-9\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-9\"\u003e 9\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-10\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-10\"\u003e10\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-0-11\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-11\"\u003e11\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-js\" data-lang=\"js\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e// Rollup configuration\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e\u003cspan class=\"p\"\u003e...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nx\"\u003eplugins\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003erollupNodeResolve\u003c/span\u003e\u003cspan class=\"p\"\u003e({\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"nx\"\u003ejsnext\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"nx\"\u003epreferBuiltins\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e      \u003cspan class=\"nx\"\u003ebrowser\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nx\"\u003erollupJson\u003c/span\u003e\u003cspan class=\"p\"\u003e(),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e...\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003e** Notice the \u003ccode\u003ebrowser: true\u003c/code\u003e **\u003c/p\u003e\n\u003col start=\"3\"\u003e\n\u003cli\u003eRun your build again: \u003ccode\u003eyarn run build\u003c/code\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e\u003cem\u003eVoila!\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca \n  href=\"https://github.com/axios/axios/issues/1259\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eSource\u003c/a\u003e\u003c/p\u003e","title":"Configure Rollup to bundle Axios module"},{"content":"To make Application Icons for both iOS and Android, use https://appicon.co/\nThe advantage of appicon.co is that after adding the base icon image, it generates the set of standard icons, and automatically downloads them to a ZIP file named: AppIcons.\nExtract the zip archive, it contains\nappstore.png: For Apple AppStore playstore.png: For Google playstore android/: A folder of standard Icons for Android Assets.xcassets: A folder of standard Icons for iOS ","permalink":"https://limistah.dev/posts/make-app-icon/","summary":"\u003cp\u003eTo make Application Icons for both iOS and Android, use \u003ca \n  href=\"https://appicon.co/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003ehttps://appicon.co/\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eThe advantage of appicon.co is that after adding the base icon image, it generates the set of standard icons, and automatically downloads them to a ZIP file named: AppIcons.\u003c/p\u003e\n\u003cp\u003eExtract the zip archive, it contains\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eappstore.png: For Apple AppStore\u003c/li\u003e\n\u003cli\u003eplaystore.png: For Google playstore\u003c/li\u003e\n\u003cli\u003eandroid/: A folder of standard Icons for Android\u003c/li\u003e\n\u003cli\u003eAssets.xcassets: A folder of standard Icons for iOS\u003c/li\u003e\n\u003c/ul\u003e","title":"How to create Application Icon (Appstore | Playstore)"},{"content":"Write Here\n","permalink":"https://limistah.dev/posts/shiny-cutting-edge/","summary":"\u003cp\u003eWrite Here\u003c/p\u003e","title":"Title of the post"},{"content":"Introduction Docker is an app development tool that eases the process of creating, running, and deploying applications. It uses the concept of containers which work just like a Virtual Machine does.\nWhile Docker runs more like a Virtual Machine does, it is more advantageous than a VM.\nIt let us define OS-like images like we are writing an actual OS that includes the only tools that we need, aside this, Docker utilizes the concept of layers which makes its images very much extensible. With this little feature, developers, sysadmins and devops engineers prefer it more. And since it has been in existence, Docker has witnessed widespread usages making it one of the defacto tool for software development, testing and delivery.\nWhile there are many low level details about Docker which we won\u0026rsquo;t be doing in this post. In this post, we will focus on creating a docker compose file that could ease the development and deployment of NodeJS based web applications which require an nginx server which acts as a proxy server to a NodeJS application, MongoDB as the database and Redis as its dependencies.\nBase Dockerfile First, we will define a Dockerfile this is a file that Docker reads to create an image which could be used to start a container.\nThis file has some interesting commands but we are only interested in a few. This file should look like below:\n1 2 3 4 5 6 7 8 9 10 11 12 13 # Base Image. # This image already exists in dockerhub. # We are just extending its functionalities FROM node:10 # Already created by the node image USER node # Changes directory in the container to this directory WORKDIR /home/node/app # Port that the node app will listen to EXPOSE 8000 The above Dockerfile commands is what we can use when we are defining an image. Images are typical to installing a fresh operating system, instead of having a full OS on a different machine, Docker builds this image to work with any computer without installing it.\nAn operating system image inside of another OS is not new, it could be found in Virtual Machine implementations. What they do is utilize a feature of computers called HyperV, it is\nSo, in our Dockerfile, we are calling a few commands that Docker could understand to create a new image for us. A brief explanation about the used commands:\nFROM: It inherits from another image, builds untop of it, making it easier to make modifications. USER: Sets the user that is associated with the current session inside of an interactive terminal in case we need one. WORKDIR: Sets the default directory that a terminal would be when it is initialized. EXPOSE: The image can receive connections, but could only be connected to by containers in the same network, except it is told to expose the port to the host machine. This command exposes a port of an image to its host machine. So, this is complete in our case. An OS with NodeJS, and we know a directory to put our source codes inside of the image, interesting.\nNext, we have to build this image to confirm if we the Dockerfile contains a valid syntax.\nTo build a Docker Image from a directory that has a Dockerfile - like ours, we can run:\n1 docker image build -t nodejs This would pull the NodeJS image from hub.docker.com then run the remaining commands to modify for our new image. When this is done, we could verify by running:\n1 docker image ls The above command would list the images currently installed locally.\nIf that works, we have successfully build our nodejs image, and we can now create containers that uses the images.\nContainers are like the computer itself, they run using an image just like our physical computers run using an operating system. In Docker, think of Images like an installable operating system, while containers to be the machine that can install an operating system, in this case an image.\nTo have a container running using the nodejs image, we can use the below command:\n1 docker container start -it --name nodejs-container nodejs The command above is starting a container, gives the container a name of nodejs, run it interactively, then use the nodejs image that we built earlier.\nThe result of the command above is a nodejs repl being launched in our terminal.\nWe are seeing that because that is what the parent image(nodejs) specified. In its Dockerfile it specifies CMD: node. We can override this easily in our docker file, but that is not a good idea, we want this setup to be reusable so we should leave that as it is. There is a better way to override it which would not require modifying the Dockerfile.\nNginx Dockerfile As a rule of thumb, we can only have one Dockerfile in a directory, and we have used that opportunity to make that file build a NodeJS image for us. But, we also need to build an nginx image from a parent image on DockerHub which would receive http requests like our webserver would.\nWhy we need an Nginx image is to bootstrap an Nginx server that would mimic our production live server. What Nginx server does in NodeJS deployments is to create a proxy against the actual node server from the rest of the world.\nLocal User \u0026ndash;\u0026gt; Our Web Server \u0026ndash;\u0026gt; Node App inside the server.\nLocal User \u0026lt;\u0026ndash; Our Web Server \u0026lt;\u0026ndash; Node App inside the server\nFor us to achieve this architecture Dockerized, it is essential we build an image off the official nginx image from DockerHub. Let\u0026rsquo;s do this!\nLet\u0026rsquo;s create a file named nginx.Dockerfile in the same directory, and have the content as below:\n1 2 3 FROM nginx:1.13 COPY nginx.conf /etc/nginx/conf.d/default.conf This is quite smaller to that of the NodeJS, but would do a full build process of the nginx image. This is the power of Docker, when we extend an image, we can add special functionalities to the image, which leaves us from the low-level details that is associated with creating that image.\nIn this nginx Dockefile, we are extending the official nginx image at version 1.13, then copying a file nginx.conf to the default nginx config path. The copy command is to ensure that we make our nginx do what we want when we are ready to create our proxy server.\nAwesome!\nNext, we can build this image as we have built the NodeJS\u0026rsquo; to very our syntaxt. But before we build, let\u0026rsquo;s add a file at the same location named nginx.conf. This file should have the following content:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 server { listen 80; location / { proxy_pass http://localhost:5000; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $server_name; } } As should be noticed in the nginx.conf file, we are redirecting all traffic coming to this server to another address(http://localhost:5000), we are assuming an app is running at this port. Now we can build the nginx Dockerfile to a Docker image with:\nd o c k e r i m a g e b u i l d - t n g i n x - f n g i n x . D o c k e f i l e We have to specify the -f nginx.Dockerfile to tell Docker to not use the default Dockerfile which is for our nodejs image.\nAnd running a container off of the image:\n1 docker container start -it --name nginx-container -p 80:80 nginx Perfect! We now have a proxy nginx server that routes request to its 127.0.0.1:5000. As we know, images are OS of their own, and the nginx would resolves localhost to itself, we don\u0026rsquo;t want it to route to itself since our nodejs app would live in the nodejs container. We will later provide a solution to this.\nMongoDB and Redis Dockerfiles We won\u0026rsquo;t be building an image for MongoDB and Redis, the major reason is that we don\u0026rsquo;t have any customization to add to the image that we would be inheriting from. So, it is more of over-engineering to have a dockerfile that contains:\nF R O M m o n g o d b : a l p i n e When we can have a docker-compose file to take away that for us or create a container on the image which would be pulled from dockerhub.\nDocker-Compose Composability is one of the many features that Distinguishes Docker from a VM. From Compose we should understand that we can create many images, containers, networks, drives on the fly in just one file. This helps with maintainance and helps to conceptualize software as a mixture of dependencies in their uniqueness.\nSo, let\u0026rsquo;s compose an architecture for NodeJS, Nginx, Mongo and Redis!\nIn the same directory, create a file named docker-compose.yml, compose files are YAML files with commands that the docker-compose command understands.\nOnce that file is created, let\u0026rsquo;s define some images that we would be needing for our architecture. First, I will define the nodejs image, making the first version of docker-compose.yml file to look like this:\nv s e e r r n s v o i i d b c p o c e u o o o n e : i c d l m r - : s l o o u m t : d n c m a s 5 \" : t k e / n : 0 2 e e s : d 0 \" x r : / : 0 t f h : : i o \" 5 l m n 0 . e e p 0 : / m 0 n D o s o d t c e a k / r e a t r p \" f p i l e In the snippet above, we are trying to create a node image through this compose file from the Dockerfile we defined above, (the context is the path to look for, while the dockerfile is a customized name for our Dockerfile, we can omit it since our nodejs Dockerfile has the default naming convention, but, we have added it for clarity sake).\nAlso, we could notice we were setting some information, if you can remember when how we create containers: docker container run --name nginx -p 80:80 nginx, we can pass parameters and further extend the image through these commands.\nHere we can see that we have forwared port 5000 from the container to the container\u0026rsquo;s 5000, map the current directory to the /home/node/app folder in the container and how we could specify the command we want to run after the container might have been started? That is the power of compose files. To know more about the commands that can be passed, see the docker-compose documentation\nNow testing our compose file, we can start the compose file in docker and not forget, as docker to build us the nodejs image since we have specified the build command:\nd o c k e r - c o m p o s e u p - b u i l d That is it, we have our first docker-compose running!\nNext, we should include our nginx config into the docer-compose file. Which would make our docker-compose.yml to look like this:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 version: \u0026#34;2\u0026#34; services: node: build: context: . dockerfile: node.Dockerfile volumes: - ./:/home/node/app command: \u0026#34;npm start\u0026#34; ports: - 5000:5000 networks: # notice this addition to the node servic - nginx-proxy # notice this - default # notice this nginx: build: context: . dockerfile: nginx.Dockerfile volumes: - ./:/home/node/app # command: \u0026#34;npm start\u0026#34; restart: always ports: - 80:80 depends_on: - node networks: - nginx-proxy - default networks: nginx-proxy: With this update, we have included the nginx.Dockerfile to build an nginx image for us which should include the nginx.conf we have. To be noticed in the bginx service is depends_on , this commands ensures that the node is started and running before nginx is started. We also added the both services to the same networks so they can communicate using their service name instead of their respective virtual ips.\nFor ngix.conf to work, we have to update the part that redirects to localhost:5000 to redirect to http://node:5000. The node hostname would be resolved to virtual ip address by Docker during runtime. This change should make our nginx.conf to look like this:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 server { listen 80; location / { proxy_pass http://node:5000; # updated from localhost to the service name proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $server_name; } } Awesome, we can build these two services and run them at the same time as we did before using: docker-compose up --build. Both services running? Great!\nIf the node service is failing, do init a node project in the root directory with: npm ini -y and add a start script OR remove the command: npm start from the docker-compose.yml file.\nWe are doing well!\nNext let\u0026rsquo;s include Redis service into the compose file.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 version: \u0026#34;2\u0026#34; services: node: build: context: . dockerfile: node.Dockerfile volumes: - ./:/home/node/app command: \u0026#34;npm start\u0026#34; ports: - 5000:5000 depends_on: - mongodb-server networks: - nginx-proxy - default nginx: build: context: . dockerfile: nginx.Dockerfile volumes: - ./:/home/node/app restart: always ports: - 80:80 depends_on: - node # network_mode: bridge networks: - nginx-proxy - default redis-server: image: \u0026#34;redis:5.0.8-alpine\u0026#34; ports: - \u0026#34;6379:6379\u0026#34; networks: nginx-proxy: It gets more interesting, but simply done. We don\u0026rsquo;t need any special configuration or extension for the Redis Image. So, we just use the image: ... command instead of the build command to tell the image we want to use. This also means that docker will not build this image, rather download it from Dockerhub.\nFinally, a database. Let\u0026rsquo;s add mongodb service to complete our setup.\nv s n e e e r r n n r m t n s v o g e o w g i i d b c p d # n i b r p d n d i p n i p o i o c e u o o o e e n u o e o e e i m o g m o r n n e : i c # # d l m r - p W - - t - - x i c d l s r - p - t - - s a r - o a r - k x : s l o o u m t e e w : l o o u t t e w - g t d g t s - : d n Y s c m a s 5 n r m o n d d n c m a s 8 n n o n d s e s \" b e s \" : p \" : t o i k e / n : 0 d n e o r g e : t k e / r : 0 d o r g e e : : 6 - : : 2 r 2 e u n e s : d 0 s o d n k i f e e s : t : s d k i f r 3 s 7 o \" x c r : / : 0 _ w i g s n a x r : / : 8 _ e s n a v \" 7 e \" 0 x t c e f h : o s o : x u t f h 0 o : x u e r 9 r m 1 y : a i o \" 5 n d - d - l : i o a n - l r e : v o 7 : n w l m n 0 : e s b p t l m l : p t : d 6 e n : . e e e p 0 p e - r . e e w r i 3 r g 2 r : / m 0 e r s o : / a o s 7 : o 7 e a n n v e x n y x : 9 : 0 n r n o s d e r y n o s y 5 \" 4 1 a e o d t s r v g d . . 7 m d e a e i e 0 2 \" e c e / r o r n / . . o . a t n x a 8 3 t m D p \" . p - - h p o p n D p a b e o c o o l i s k d c p o f i e e k i n i n r e n i l g f a r e c e i n f \" \" d l d i t i e l o f m e f o m e n a r g k e o e n d t b i t i s m e t a r e g v l e e l s r w a h n a d t n i o t t d j o u e s s t . a s i g l e i m a g e As simple as the redis service is, same with the mongodb service. We are downloading the image from github and running a mongodb-server container off it.\nGreat work!\nNow, we can test our setup as we always do: docker-compose up --build.\nSpeaking between containers. If we try to connect to the redis service from our node app we might get an error as the address 127.0.0.1 would mean the instance itself and not the host computer, same will happen with the mongodb service.\nTo solve this, we will add a service_name to every service we want to reference inside of another container, and ensure that the depends_on is updated to that new service name.\nSo our final config would look like this:\nv s n e e e r r n n r m t n s v o g e o w g i i d s b c p d # n i s b r p d n d s i p n s i p o i o c e e u o o o e e n e u o e o e e i e m o g e m o r n n e : r i c # # d l m r - p W - - t - - x r i c d l s r - p - t - - s r a r - o r a r - k x : s v l o o u m t e e w : v l o o u t t e w - v g t d v g t s - : e d n Y s c m a s 5 n r m o n d i d n c m a s 8 n n o n d s i e s \" b i e s \" : p \" r : t o i k e / n : 0 d n e o r g e c : t k e / r : 0 d o r g e e c : : 6 - c : : 2 r 2 _ e u n e s : d 0 s o d n k i f e e e s : t : s d k i f r e 3 s e 7 o \" n x c r : / : 0 _ w i g s n a _ x r : / : 8 _ e s n a v _ \" 7 e _ \" 0 x a t c e f h : o s o : x u n t f h 0 o : x u e n r 9 r n m 1 y m : a i o \" 5 n d - d - l a : i o a n - l r a e : v a o 7 : e n w l m n 0 : e s b p t m l m l : p t : m d 6 e m n : : . e e e p 0 p e - r e . e e w r e i 3 r e g 2 r : / m 0 e r s o : : / a o : s 7 : : o 7 n e a n n v e x n y x : 9 : 0 o n r n o s d e r y n n o s y r 5 \" m 4 1 d a e o d t s r v g g d e . o . 7 e m d e a e i i e d 0 n 2 \" e c e / r o r n n / i . g . o . a t n x x a s 8 o 3 t m D p \" . p - d - h p o p n D p a b b e o c o o l i s k d c p o f i e e k i n i n r e n i l g f a r e c e i n f \" \" d l d i t i e l o f m e f o m e n a r g k e o e n d t b i t i s m e t a r e g v l e e l s r w a h n a d t n i o t t d j o u e s s t . a s i g l e i m a g e Easy enough? Now in our source codes we can use http://redis:6379 to connect to redis service container and http://mongodb:27017 to connect to the mongodb service container.\nVoila!\nConclusion Finally, we cracked it. Walking through the journey from an empty file to a docker-compose.yml file that we can share with anyone that would love to replicate same setup for their development. This was so much fun to walk through.\nThere is more to Docker than we have covered, don\u0026rsquo;t hesitate to read the docs for more information and commads that could be used in Dockerfile and docker-compose.yml files.\nPLEASE!!!\nDockerize yourself at this time, stay safe, stay home, stay in a containerized environment!!!\nOu re voir!\n","permalink":"https://limistah.dev/posts/docker-node-nginx-redis-mongodb/","summary":"\u003ch2 id=\"introduction\"\u003eIntroduction\u003c/h2\u003e\n\u003cp\u003eDocker is an app development tool that eases the process of creating, running,  and deploying applications. It uses the concept of containers which work just like a Virtual Machine does.\u003c/p\u003e\n\u003cp\u003eWhile Docker runs more like a Virtual Machine does, it is more advantageous than a VM.\u003c/p\u003e\n\u003cp\u003eIt let us define OS-like images like we are writing an actual OS that includes the only tools that we need, aside this, Docker utilizes the concept of layers which makes its images very much extensible. With this little feature, developers, sysadmins and devops engineers prefer it more. And since it has been in existence, Docker has witnessed widespread usages making it one of the defacto tool for software development, testing and delivery.\u003c/p\u003e","title":"Dockerized Node/Nginx, MongoDB, Redis app setup"},{"content":"Introduction SocketIO is a JavaScript library that makes developers\u0026rsquo; lives easier when dealing with web socket and socket programming. This is the fact that SocketIO has abstracted out all the low-level and tedious steps that are associated with setting up a socket server and client; it has made the question of programmers be \u0026ldquo;How can I structure my application.\u0026rdquo;\nWhile I have done different types of socket implementations, I will walk us through a setup that has always work for me and has proven to be the best in cases that I have had to use SocketIO.\nIn this post, we will be implementing a basic SocketIO server, set up a small database for our users, have a client that consumes our application.\nIt will never be as dull as you think, I promise.\nI have created a repo for the setup and could be found here.\nInstallation \u0026amp; bootstrapping So, to begin with, I will initialize a new repository for the setup for clarity reasons.\n1 2 3 4 mkdir socketio-setup cd ./socket-io-setup git init npm init -y In the above code snippet, we are trying to bootstrap our folder structure and codebase. The first line creates a new directory in our local hard drive. We changed the current directory into the newly-created directory. We initialized a new but empty GitHub repo. In the last line, we initialized a new npm project using npm init -y and accepting the default config through the -y flag.\nTo further complete our initializations, we will install socketIO, add .gitignore to exclude some noisy folders, and add our first commit so far for our new repo, smiles.\n1 2 3 4 5 npm install -S socketio express touch .gitignore echo \u0026#39;/node_modules\u0026#39; \u0026gt; .gitignore git add . git commit -am \u0026#34;Initial commit\u0026#34; After completing our project folders\u0026rsquo; initialization, we should attempt to bootstrap a basic socket server. To accomplish this, we will first create a folder called src in the root of the project and make index.js the sole file in the folder. Having done that, we can add some code into the src/index.js file.\nWe will first of all create a basic express server and initialize socketIO library:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // Copied from https://socket.io/get-started/chat/ var app = require(\u0026#34;express\u0026#34;)() var http = require(\u0026#34;http\u0026#34;).createServer(app) var io = require(\u0026#34;socket.io\u0026#34;)(http) app.get(\u0026#34;/\u0026#34;, function (req, res) { res.send(\u0026#34;\u0026lt;h1\u0026gt;Socket IO project folder setup\u0026lt;/h1\u0026gt;\u0026#34;) }) io.on(\u0026#34;connection\u0026#34;, function (socket) { console.log(\u0026#34;a user connected\u0026#34;) }) http.listen(3000, function () { console.log(\u0026#34;listening on *:3000\u0026#34;) }) And with the above, we have successfully created a basic socketIO server. Hurray!\nEvents \u0026amp; Listeners Events and Listeners are two of the basic concepts that are significantly related to SocketIO library.\nListeners With Listeners the client(s) could tell the server that something should happen in the server. A basic example of a listener is when a user is connected or disconnected:\n1 2 3 4 5 6 7 8 9 // Connection event io.on(\u0026#34;connection\u0026#34;, function (socket) { console.log(\u0026#34;User with socketId %s connected\u0026#34;, socket.id) }) // Disconnection event io.on(\u0026#34;disconnect\u0026#34;, function (socket) { console.log(\u0026#34;User with socketId %s disconnected\u0026#34;, socket.id) }) Simply put, a listener is a block of code that a client tells the server to run after the server might have registered the listener with a name.\nTo register a listener, we only have to call the .on function on the io object. The very many ways to call this function are properly documented on the socketio\u0026rsquo;s website.\nEvents Somethings might happen on the server that it might be so exciting to want to tell a client. When we decide that a client should know of something, we are triggering an event.\n1 socket.emit(\u0026#34;hello\u0026#34;, \u0026#34;can you hear me?\u0026#34;, 1, 2, \u0026#34;abc\u0026#34;) We emit on the socket, as that socket needs to know about the event that just happened. Nevertheless, we can emit any socket, sockets or room space, etc., and emit cheatsheet exists for this purpose.\nApplication folder structure Having understood the basic concepts of Events and Listeners, it is so glaring that we can have all of the listener and events in just a file say the src/index.js file.\nOur code could be messed up and look more like this\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 //... // Listener 1 io.on(\u0026#34;someEventName_1\u0026#34;, function (socket) { console.log(\u0026#34;someEventName_1 with socketId: %s\u0026#34;, socket.id) socket.emit(\u0026#34;someEventName_1\u0026#34;, { message: \u0026#34;Success\u0026#34; }) //... }) // Listener 2 io.on(\u0026#34;someEventName_2\u0026#34;, function (socket) { console.log(\u0026#34;someEventName_2 with socketId: %s\u0026#34;, socket.id) socket.emit(\u0026#34;someEventName_2\u0026#34;, { message: \u0026#34;Success\u0026#34; }) //... }) // Listener 3 io.on(\u0026#34;someEventName_3\u0026#34;, function (socket) { console.log(\u0026#34;someEventName_3 with socketId: %s\u0026#34;, socket.id) socket.emit(\u0026#34;someEventName_3\u0026#34;, { message: \u0026#34;Success\u0026#34; }) //... }) //... Interesting to note is that some handlers for some listeners can get so large. So, what do we do?\nThis is what we are trying to solve in this post. Firstly, let\u0026rsquo;s create a folder for our listeners and add an index.js file into it.\n\u0026ldquo;`bash mkdir src/listeners touch ./src/listeners/index.js\nT M ` m } h o ` o e v ` d i } i j u o ) ` n s l . s s g e o o r . n c c t e ( k / h x \" e l e p c t i o o . s ` r n e t c t n m e o s e i n n c t e n = t ( r e i \" s c f o c ` t u n o i n \" n h o c , n a n t e v ` i f c e o u t e n n e d v c d i e ( t \" s n i i , t t o o i ) n s n t o c o { ( c t s k a o e m n c t o k ) d e e u v t l e ) e n s t { t m h o a d t u l e e x p w o o r u t l d j u i s n t a a f f i u l n e c t l i o o c n a t a e n d d a a t c c ` e s p r t c / a l n i s ` t i e o n ` e r p s a / r c a o m n e n t e e c r t , i o t n h . e j s s ` o , c k l e o t o I k O l o i b k j e e c t t h . i s T : h e i n d e x f i l e w i l l b e r e s p o n s i b l e f o r t h e i n i t i a l i z a t i o n o f t h e s e m o d u l a r e v e n t s f i l e s . In the above module, we are exporting a function that accepts the io parameter. In the listener\u0026rsquo;s body, we are trying to tell the socket that it has connected by emitting a connected event. Simple enough!\nMoving forward, we can now import this new listener module in the index.js at the listener\u0026rsquo;s directory; then, we can write our bootstrapping code for the listener.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 // src/listeners/index.JS module.exports = (io) =\u0026gt; { const fs = require(\u0026#34;fs\u0026#34;) const path = require(\u0026#34;path\u0026#34;) // Full path to the current directory const listenersPath = path.resolve(__dirname) // Reads all the files in a directory fs.readdir(listenersPath, (err, files) =\u0026gt; { if (err) { process.exit(1) } files.map((fileName) =\u0026gt; { if (fileName !== \u0026#34;index.js\u0026#34;) { console.debug(\u0026#34;Initializing listener at: %s\u0026#34;, fileName) // Requires all the files in the directory that is not a index.js. const listener = require(path.resolve(__dirname, fileName)) // Initialize it with io as the parameter. listener(io) } }) }) } In the above code, we are only trying to ensure that all files in the listener directory are required and run with an io object as the parameter. The whole src/listeners/index.js is being exported as a function to ensure that we only run the module when we need and that the io parameter is being passed down.\nWith this arrangement, subsequent listeners would only require us to create a file inside the src/listeners directory, function as the main export, and accept io as the sole parameter.\nNext, we have to import the src/listeners/index.js in the src/index.js file. To do this, the src/index.js file will look like this.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 // Requires the listener directory(index.js file) const initListeners = require(\u0026#34;./listeners\u0026#34;) var app = require(\u0026#34;express\u0026#34;)() var http = require(\u0026#34;http\u0026#34;).createServer(app) var io = require(\u0026#34;socket.io\u0026#34;)(http) app.get(\u0026#34;/\u0026#34;, function (req, res) { res.send(\u0026#34;\u0026lt;h1\u0026gt;Socket IO project folder setup\u0026lt;/h1\u0026gt;\u0026#34;) }) // Calls it with the io object. initListeners(io) http.listen(3000, function () { console.log(\u0026#34;listening on *:3000\u0026#34;) }) And so far, we have just bootstrapped event listeners to events that could be emitted from the client.\nWhen we need to add a new event listener, we just have to define it in a file in the /src/listeners directory, its full path should be: src/listeners/someNewEvent.js, while its basic content would be:\n1 2 3 4 5 module.exports = function (io) { io.on(\u0026#34;someEventFromClient\u0026#34;, function (socket) { socket.emit(\u0026#34;responseToSomeEventFromClient\u0026#34;, { data: {}, socket }) }) } That is all about listeners; next is for our events.\nWe have seen that in the listeners, we were sending some events back to the client. This might suffice for a tiny application; let\u0026rsquo;s consider a scenario:\nSuppose when a new event is sent to the server, we need to pull the user information from the database, make some adjustments, and send them a new copy of the user data.\nIn the scenario above, it is sufficing enough to have all the user manipulation in the listener for the event, but giving the user the updated information is a task that should be done inside of an event emitter, a major reason being that we can reuse this event emitter and also maintain consistent naming across the codebase (both frontend and backend)\nGetting our hands cleaned away from COVID-19, firstly, let\u0026rsquo;s define a folder like so src/events, and add our index file. The content of the event should look like this:\n\u0026ldquo;`js // src/events/index.js\nmodule.exports = io =\u0026gt; { const fs = require(\u0026ldquo;fs\u0026rdquo;); const path = require(\u0026ldquo;path\u0026rdquo;);\nconst eventsPath = path.resolve(__dirname);\nfs.readdir(eventsPath, (err, files) =\u0026gt; { if (err) { console.error(err); process.exit(1); }\nfiles.map(fileName =\u0026gt; { if (fileName !== \u0026quot;index.js\u0026quot;) { module.exports[fileName] = require(path.resolve(__dirname, fileName)); } }); }); };\nT N ` c } m h e ` o o e x ` n s d t j s o u c , s t c l o D k e d l e o e . e e v t e t e s . x a ' n o e p b s t m m o o e i r v d = t t e e i ( s f ( n \" i i s t s = s n o e o e c r c e s k e k v i o e s e e m u t t r n i r ) i \" t l n , a _ = g r c \u0026gt; { o t s t n { h o o n i c e n k w c g e h t t a e i , t d n _ s w i e e d v e S d e o e n o m f t f e i , n t o e w h t d h i h i s e i c r n h p l i ` w a n s i c t r l e e c l ! r / e l e s i m t s i i t t n e g n t e h d r e a s t / c a i l , n i d e m e n a x t y . b j o e s n * ` c / , e } ) t t h h e e d s i o f c f k e e r t e n h c a e s i b n e e t n h i c s o n c n a e s c e t e i d s . t S h o a , t w w e e w a i r l e l e c x r p e o a r t t e i n a g n a e l w l f t i h l e e f ` i s l r e c s / e i v n e n t t h s e / c ` o s n r n c e / c e t v e e d n . t j s s ` ` f a o n l d d e h r a v a e s i i d t e l t o h o e k ` l i i n k d e e x t . h j i s s ` f i l e . A w e s o m e ! Simple! We have bootstrapped our event files with separation of concerns being considered.\nThe next question, how do we consume events in our listeners with this structure. For this case, let\u0026rsquo;s make a modification to src/listeners/connected.js to be:\n1 2 3 4 5 6 7 const { connectedEvent } = require(\u0026#34;../events\u0026#34;) module.exports = function (io) { io.on(\u0026#34;connection\u0026#34;, function (socket) { connectedEvent(socket) }) } First, we import the connectedEvent from the events module, and in line 5, we move changed calling emit to calling the imported event module.\nAwesome, right?\nNext, we can focus on building our application around this ultra-simple architecture and still maintaining it in the future.\nConclusion In this post, we have been able to bootstrap a socketIO application, ensure that we have separation of concerns all through.\nWe could add more improvement by ensuring that the names of the events come from a single file. This can assist us in having consistency and reducing the effort when it is time for documentation.\nSometimes, we will focus a post on ensuring that we persist the socket object after it has been initialized.\nKeep hacking, wash your hands regularly, stay safe, stay at home!\n","permalink":"https://limistah.dev/posts/socketio-app-structure/","summary":"\u003ch2 id=\"introduction\"\u003eIntroduction\u003c/h2\u003e\n\u003cp\u003eSocketIO is a JavaScript library that makes developers\u0026rsquo; lives easier when dealing with web socket and socket programming. This is the fact that SocketIO has abstracted out all the low-level and tedious steps that are associated with setting up a socket server and client; it has made the question of programmers be \u0026ldquo;How can I structure my application.\u0026rdquo;\u003c/p\u003e\n\u003cp\u003eWhile I have done different types of socket implementations, I will walk us through a setup that has always work for me and has proven to be the best in cases that I have had to use SocketIO.\u003c/p\u003e","title":"SocketIO - App structure and architecture"},{"content":"In computation systems, names like concurrent, sequential, parallel, serial, synchronous, asynchronous, non-blocking, shared state, message passing, and likes, stand as a forbearer for the actual task that happens in a system.\nWhile all of the above techniques have their use cases, in the world of JavaScript, asynchronous and synchronous programming never leave the tongues of its programmers.\nIn his Concurrency glossary, slikts (dabas@untu.ms) wrote about asynchronous, he said:\nAsynchrony means \u0026ldquo;not happening at the same time\u0026rdquo;, and asynchronous message passing is a communication model that does not require the sending and receiving to be synchronized, meaning that the sender isn\u0026rsquo;t blocked until the receiver is ready.\nIt might not be clear; still, we will go into a later detail about this with a real-life example. Don\u0026rsquo;t tell anyone, life is mostly asynchronous.\nWhile languages like Java, C#, C++, etc. run their computation on their main thread and could spawn out a new thread to run another set of instructions which are in parallel to the main thread and could also communicate with it. JavaScript, in its uniqueness, does not support that model of computation.\nIn JavaScript, all computations and instructions run in a sequence (i.e., one after another) in a single thread. This means that for instructions ranging from A, B, C, D, E to be executed, A would be executed first and when done, B is then executed, then C, then D, then E, which sees the program to its end.\nStill, with the single-threaded nature of JavaScript, there is a unique feature that makes it outstanding, it is its non-blocking I/O model.\nI/O(Input/Output) could be in any form, like fetching of data over the internet, getting a file from the filesystem and likes, all of these processes does not block the main thread from continuing its execution.\nA real life case study In high school, when students are given an assignment, they are required to submit it. Once they submit the task to the teacher, they are expected to wait until their books are returned back to them already marked/graded.\nWhile they had submitted the assignments and are waiting for the results, they could do other things like reading, attend classes, play games, and joke with friends.\nOnce the teacher is done marking, the students are notified, then they would go to the teacher to pick them up, completing the process.\nIn this scenario, the students have done a task asynchronously. While the teacher could only do one thing at a time, they have asked the teacher to teach them and still mark their assignments.\nThe teacher could not complete both of them at the same time, so they would have to wait, but, set a trigger (through the class rep/governor) that made them know that the assignments are ready and the results are out.\nThe notification could help the students to determine what next to do. Maybe, to proceed with putting their books into their bags or doing the corrections or learning from other classmates. (whatever is next)\nThat is what happens in asynchronous programming.\nAsynchrony in JavaScript JavaScript as a language comes with support for asynchronous programming; there have been different ways to come up with this. Most common is the callbacks, which could be seen in many legacy code bases.\nIn the modern JavaScript engine, there is an added support for writing asynchronous code. We would be exploring these options and their demerits, and I would conclude with why I think that Async/Await as a feature of the language is too essential to understand. Let\u0026rsquo;s hit the start knob.\nCallbacks For seasoned developers, this might seem like unveiling a nightmare following their experiences with callback hell. But, a callback is not that bad.\nA callback is a function passed during a function call/invocation to be executed when the called function determines. A basic example is what setTimeout, setInterval do. Once any of them get called, it takes the function and executes it at the specified time passed from their second argument.\n1 2 3 4 5 setTimeout(() =\u0026gt; console.log(\u0026#34;This is a callback 1 sec. setTimeout test\u0026#34;), 1000) setInterval( () =\u0026gt; console.log(\u0026#34;This is a callback 1 sec. setInterval test\u0026#34;), 1000 ) The two function calls above receive a callback as their first argument and execute it once they each reach the time (1sec) in their second argument. They will never obstruct the main thread from continuing executing other functions; instead, an entry is made in the call stack to take care of this.\nAnother great example is in the DOM Events API. A basic onclick event could be listened to like this:\n1 2 3 4 const button = document.getElementById(\u0026#34;#submitButton\u0026#34;) button.addEventListener(\u0026#34;click\u0026#34;, () =\u0026gt; { console.log(\u0026#34;Submit Button Clicked\u0026#34;) }) Once the button is clicked, with the single thread nature of JavaScript, one would expect that the above code should work like this:\nStore the DOM reference of submitButton to the button variable. button.addEventListener is called which puts in an entry into the call stack The event is registered against a click name, and the code for the addEventListener is executed. Once it is executed, the function/handler should be triggered immediately in the addEventListener execution context As stated in the above, we expect all of those to happen in parallel. But, what happens there is that code and functions are delegated to a later time when an actual action has happened. Recall the High School Assignment illustration we gave?\nWhat happens is that:\nOnce the click event has been triggered on the button, the function would be executed. And this could happen any time in the future, but other code could still run while waiting for this.\nGood Ol\u0026rsquo;days of AJAX With the myriads of NPM libraries that make this happen in a few lines of JS code. Also, the inclusion of the fetch API to the modern JavaScript engines has made this to be so uncommon to the modern developers what AJAX means right in the browser, as they take away the underlying fact about AJAX web requests.\nBefore the days of EcmaScript 6, while developers still really disliked working with JavaScript (which is unavoidable for web developers, though 😄), web requests could be made over the internet through an XMLHTTPRequest object. The technique for making this request was later formed to be called Asynchronous JavaScript and XML, popularly known as AJAX.\nWhat AJAX means is that we could request the internet, wait for the result to come while still having other things going on in the browser (basically, a loading icon). This is asynchronous in nature, and the main reason why it is called AJAX.\nAJAX uses the callback model, a basic vanilla AJAX request using the XMLHTTPRequest object looks like this:\n1 2 3 4 5 6 7 8 9 10 // Picked from https://www.w3schools.com/xml/xml_http.asp var xhttp = new XMLHttpRequest() xhttp.onreadystatechange = function () { if (this.readyState == 4 \u0026amp;\u0026amp; this.status == 200) { // Typical action to be performed when the document is ready: document.getElementById(\u0026#34;demo\u0026#34;).innerHTML = xhttp.responseText } } xhttp.open(\u0026#34;GET\u0026#34;, \u0026#34;filename\u0026#34;, true) xhttp.send() It is not attractive, I know. If you are a modern JavaScript developer, respect those that have passed through this and still look uncool to you, this was so cool to them back then.\nThis AJAX feature utilized the model of event handling and was very easy to comprehend by JavaScript developers. But, it shares a similar problem with all callback-based coding patterns/techniques.\nCallback Hell Does it mean hell in our code/machines, well, not necessarily? It is something exciting to know some of its details.\nImage source\nLet\u0026rsquo;s tell of a story of a codebase that illustrates this.\nFirst let\u0026rsquo;s define a function that takes a callback. It would add two numbers together, then pass the result to a function defined as the third argument, clear enough? I believe!\n1 const sumTwoNumbers = (a, b, doneCB) =\u0026gt; doneCB(a + b) Next, let\u0026rsquo;s determine an Array of numbers but try\n1 const numbers = Array(1, 2, 3, 4, 5) Now, we can add up the numbers successively using Array.prototype.forEach:\n1 2 3 4 5 6 const totalAdditions = 0 numbers.forEach((number, index) =\u0026gt; { sumTwoNumbers(number, numbers[index + 1], (total) =\u0026gt; { totalAdditions += total }) }) As seen above, we are just two levels deep into the callback calls, and it is not getting any interesting. It should be noted that as the callback levels increase, the risk of hitting a re-initialization of variables increases. The first callback might have initialized numbers re-initializing it might disrupt the logic of the code, who knows?\nAnd looking at an example from the below image, we can see that it can get so uninteresting in larger applications.\nPromises to the rescue Eric Elliott: A promise is an object that may produce a single value some time in the future: either a resolved value, or a reason that it’s not resolved (e.g., a network error occurred).\nIn JavaScript, Promises have made the life of programmers more easier in handling asynchronous code. With Promises, most of the underlying problems like callback hells are waved out.\nPromises has a very interesting background in JavaScript, Eric Elliott gave has a great information about promises, regarding that, a promise could be constructed in a few lines of code:\n1 2 3 let promise = new Promise(function (resolve, reject) { // executor }) In constructing a promise, we call the Promise constructor with a function called the executor. This function(executor) receives two arguments labeled as resolve and reject, both of which are functions.\nThe resolve argument is expected to be called with a value resolve(value) only when the executor has finished doing its tasks without any error. While the reject is expected to be called with a value (usually of type Error) reject(error) only if the promise failed or faced an error.\n1 2 3 4 5 6 7 8 let promise = new Promise(function (resolve, reject) { try { const value = 2 * 2 resolve(value) } catch (e) { reject(e) } }) There are three (3) notable methods for every Promise object.\n.then() Accepts a function that receives the value passed when resolve is called by the executor. Only get called when the executor calls resolve. .catch() Accepts a function that receives the value passed when reject is called by the executor. Only get called when the executor calls reject. .finaly() The object returned by every initialization of a Promise constructor has some special attributes and methods. One of this is the status promise.status property, which tells about the current state of the Promise.\nThe status properties has three (3) possible values:\nPending: This is a state before resolve() is called. Resolved: This is a state after resolve() has been called. Rejected: This is a state after reject() has been called, or an error is thrown in the executing function. Promise are chainable, hence, every .then() called on a promise object returns another promise.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const promise = new Promise(function (resolve, reject) { try { const value = 2 * 2 resolve(value) } catch (e) { reject(e) } }) // Consuming the promise promise .then((value) =\u0026gt; value * 4) .then((value) =\u0026gt; value * 6) .then((value) =\u0026gt; console.log(value)) While consuming the promise, every resolver in the .then() function returns a value until the last call to .then(). Failure to do this as we have done in the last call of .then() would make the next call to .then() receive an undefined.\n1 2 3 4 5 6 7 // Consuming the promise promise .then((value) =\u0026gt; value * 4) .then((value) =\u0026gt; value * 6) .then((value) =\u0026gt; console.log(value)) .then((value) =\u0026gt; console.log(value) /* -\u0026gt; undefined */) We can use the finally in many different ways since it is called irrespective of the state of the Promise. The most obvious case of using it is when we need to hide a loading icon to show a message for the result of the action that has just been carried out.\n1 2 3 4 5 6 7 8 9 10 promise .finally(() =\u0026gt; { document.querySelector(\u0026#34;.loading-icon\u0026#34;).remove() }) .then((data) =\u0026gt; { // consume data }) .catch((err) =\u0026gt; { // Show error }) There is more to Promises, this only gives a preamble and brief introduction to it, a thorough explanation could be found here\nAsync / Await It is not surprising to note that, for some unknown reasons that are best known to developers, they want Promises to be synchronous.\nThey found themselves in a situation where they need the result of a Promise to be available before they proceed to the next chunk of code - Either it is a network request, or a result from an extensive math function, they just needed the result to be available right in the next line of their code.\nTo solve this for them, the folks at TC39 gave us Async/Await.\nThis feature assists in constructing a promise right from a function, and we could wait for the result of a Promise before moving to the next line of code.\n1 2 3 4 5 6 7 const getNumber = () =\u0026gt; { return async () =\u0026gt; { return 20 } } const result = await getNumber() Looking closely at the code above, we could see that the getNumber function returns an async function(a Promise), but why not make the returned function the primary function for getNumber?\nNo, we can not. JavaScript does not support making a global function an async function; the best we can do is wrap it in a containing function and make the inner function returns an async function. If we need a global function to be a Promise, it is best that we use a Promise constructor to define it.\nWhen we called const result = await getNumber(), we are telling the engine not to run any other code until we have the result from the Promise returned by getNumber, in this case, 20. If we omit await from that expression, following code will run while running the Promise returned by getNumber in the background.\nIn my honest opinion, I see async as syntactic sugar to constructing a Promise on the fly. If we would want to attempt to have the getNumber as a promise constructor instead of an async/await we would have:\n1 2 3 4 5 6 7 const getNumber = new Promise(function (resolve, reject) { try { setTimeout(() =\u0026gt; resolve(20), 60 * 1000) } catch (e) { reject(e) } }) In the above code, we have waited for about 1minute for the promise to be resolved if at all no error is thrown in our executor.\nDo you want to learn more about Async/Await and be a Ninja of asynchronous javascript? Try seeing the MDN guide on the topic.\nConclusion As seen above, we can deduce that the underlying engine of JavaScript supports asynchronous coding. At the same time, this is great; it created some problems for developers trying to make their code asynchronous.\nAs a solution to the challenge, Promise was introduced into the language, which was followed by the inclusion of Async/Await construct.\nIn my honest opinion, writing Async/Await in my codebases has made my code not just readable, but very predictable.\nSo, to asynchronous Javascript coding, I highly recommend understanding Promises and Async/Await construct if at all we want our future self and other developers to say a prayer when they see our code after we write it.\nShalom!\n","permalink":"https://limistah.dev/posts/asynchronous-javascript/","summary":"\u003cp\u003eIn computation systems, names like concurrent, sequential, parallel, serial, synchronous, asynchronous, non-blocking, shared state, message passing, and likes, stand as a forbearer for the actual task that happens in a system.\u003c/p\u003e\n\u003cp\u003eWhile all of the above techniques have their use cases, in the world of JavaScript, asynchronous and synchronous programming never leave the tongues of its programmers.\u003c/p\u003e\n\u003cp\u003eIn his \u003ca \n  href=\"https://slikts.github.io/concurrency-glossary/?id=asynchronous-vs-synchronous-non-blocking-concurrent-vs-blocking-sequential\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003e\u003cstrong\u003eConcurrency glossary\u003c/strong\u003e\u003c/a\u003e, \u003cstrong\u003eslikts (\u003ca \n  href=\"mailto:dabas@untu.ms\"\n  \u003edabas@untu.ms\u003c/a\u003e)\u003c/strong\u003e wrote about asynchronous, he said:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eAsynchrony means \u0026ldquo;not happening at the same time\u0026rdquo;, and asynchronous message passing is a communication model that does not require the sending and receiving to be synchronized, meaning that the sender isn\u0026rsquo;t blocked until the receiver is ready.\u003c/p\u003e","title":"Asynchronous Javascript"},{"content":"You might have been in this kind of trap before or currently in one, well, I just want to tell you that I know your pain.\nIn a custom built CMS, managing of Menu and navigation in the site from the Admin Dashboard is a requirement. While it is interesting to use, it is not as interesting to build.\nI just walked past this process, here, I am sharing how I have conquered it.\nTo consider I have a limitation for the menu to be deeply nested not more than two steps, so in this case, this is to ensure that the loops that I will write won\u0026rsquo;t have to be too recursive - which if not properly handled could create havoc.\nModel/Database In my database, I have the menu store as a flat document with each of them having their own information. The menu model has the following properties:\nposition: Position of the menu (Determined by the application, in my case, \u0026ldquo;Main Nav\u0026rdquo;, and \u0026ldquo;Footer Menu\u0026rdquo;) **name:**Text that the user would see parent: String containing the index of the parent or index of the grand parent with the index of the parent separated by a dot in the case of nesting url: The URL to map the navigation to weight The sorting order for the menu. So, with the above fields, I can create a menu like this:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 const mainMenu = [ { name: \u0026#34;Home\u0026#34;, url: \u0026#34;/\u0026#34;, position: \u0026#34;Main Nav\u0026#34;, parent: null, weight: 1 }, { name: \u0026#34;Listings\u0026#34;, url: \u0026#34;/listings\u0026#34;, position: \u0026#34;Main Nav\u0026#34;, parent: null, weight: 2 }, { name: \u0026#34;Categories\u0026#34;, url: \u0026#34;/categories\u0026#34;, position: \u0026#34;Main Nav\u0026#34;, parent: null, weight: 3 }, { name: \u0026#34;Pricing\u0026#34;, url: \u0026#34;/pricing\u0026#34;, position: \u0026#34;Main Nav\u0026#34;, parent: null, weight: 4 }, { name: \u0026#34;Pages\u0026#34;, url: \u0026#34;#\u0026#34;, position: \u0026#34;Main Nav\u0026#34;, parent: null, weight: 5 }, { name: \u0026#34;About\u0026#34;, url: \u0026#34;#\u0026#34;, position: \u0026#34;Main Nav\u0026#34;, parent: 5, weight: 1 }, { name: \u0026#34;Contact\u0026#34;, url: \u0026#34;/contact\u0026#34;, position: \u0026#34;Main Nav\u0026#34;, parent: \u0026#34;5.1\u0026#34;, weight: 1 } ].map(async menu =\u0026gt; { const mainMenu = new Menu(); Object.assign(mainMenu, menu); // Populates the fields to the mainMenu object await mainMenu.save(); // Saves the menu }); First problem has been solved.\nNext problem is making the data of the menu available in a format that we can render more easily.\nMenu parser middleware To solve this, I created a middleware which sets a global value for my view engine. The code in the middleware looks like this:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 async handle({ request, view }, next) { // call next to advance the request // Can be MongoDB Model, this is AdonisJS const MenuService = use(\u0026#34;App/Services/Admin/MenuService\u0026#34;); const menuService = new MenuService(); const menus = await menuService.findAll(\u0026#34;position\u0026#34;); // Parsing of the menu comes in here // At this point built menu contains keys of menu positions, the value of every keys is an array of nested values that can be passed to drag and drop UI libraries like JQuery nestable for admin configuration using a browser. // Adonis code to share values across all views. view.share({ siteMenus: builtMenu }); } Parsing of Zero Level menus and sorting After the middleware has been defined, first is to understand that our menu won\u0026rsquo;t go more than two levels, with the zero level being the parent to other menus. To get the parent out of the menu, we map through all the items in the menus variable, getting all the elements that has just no dot in their parent value then storing them at a position defined in the builtMenu object\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const builtMenu = {}; const menuWithParent = []; // Stores all menu that has parent for later processing // First of all, gather all the menus with same position to the same index menus.map(menu =\u0026gt; { // The position for the current menu does not exist in the builtMenu object if (!builtMenu[menu.position]) { // Create it with an empty array builtMenu[menu.position] = []; } // If this menu does not have parent, push it directly into its position key in the builtMenu variable if (!menu.parent) { builtMenu[menu.position].push(menu); } else { // Else, push for later processing menuWithParent.push(menu); } }); Before we move forward, we should add code that sorts the generated menu so far, the easiest way is to get the keys in the built menu and map through each keys, then running the sort aggregation method on each item of the builtMenu data.\nThis sorting only compares the weight of each menu item, these weights are expected to go in ascending order, hence we can use a.weight \u0026gt; b.weight so that we can have lowest number comes first ahead of larger number\n1 2 3 4 5 6 7 8 const sortedMenu = {}; // Holds all the menu that has been sorted // Sorts all the menu in their respective positions, just to maintain ordering Object.keys(builtMenu).map(positionKey =\u0026gt; { const menuAtPosition = builtMenu[positionKey]; sortedMenu[positionKey] = menuAtPosition.sort( (a, b) =\u0026gt; a.weight \u0026gt; b.weight ); }); At this stage, our builtMenu should look like this:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 { \u0026#34;Main Menu\u0026#34;: [ { ...menu // \u0026lt;- Menu details }, { ...menu // \u0026lt;- Menu details }, { ...menu // \u0026lt;- Menu details }, { ...menu // \u0026lt;- Menu details }, { ...menu, // \u0026lt;- Menu details } ] } What we have to do next is work with the menus that exist in the menuWithParent variable that was gathered while pulling the menus that do not have child/grandchild.\nParsing of First Level menus and sorting To do this, we map through all the item in the variable, ensure that it does not have grandparent, if it does we push it to the menuWithGrandParent array for later processing, if it does not, we append the menu to the parent after creating a children array in to the parent if children array does not exist in the parent.\nWe also sort the menu generated so far after pushing to the parent, to maintain ordering of the menu.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 // This variable holds the menus that has grandparent const menuWithGrandParent = []; // We want to work on the menu that has parent menuWithParent.map(menu =\u0026gt; { // following the structure of our menu in the database, parentIndex is the value of the parent key of every menu that has parent let parentIndex = menu.parent; // We still have to determine if the menu is a grandchild of a parent, so we split by the dot separator that we have used then use the length of the array as a determinant const hasGrandChild = parentIndex.split(\u0026#34;.\u0026#34;).length \u0026gt; 1; // This menu is not a grandchild, we can proceed with our process if (!hasGrandChild) { // We need to get the position of the menu const menusAtPosition = builtMenu[menu.position]; parentIndex = parseInt(parentIndex); // Decrements the parentIndex to get the index in the array, in any case the reducing by 1 is lesser than zero, we want to default to index 0 else we want to get the index in the array const parent = menusAtPosition[parentIndex - 1 \u0026lt; 0 ? 0 : parentIndex - 1]; // If the parent does not have children, we to initialize that to an array if (!parent.children) { parent.children = []; } // Push the new child of the parent into its children parent.children.push(menu); // Sort the children in the parent to maintain ordering parent.children.sort((a, b) =\u0026gt; a.weight \u0026gt; b.weight); } else { // This menu has a grandparent, we can not process it like this, we push to the menuWithGrandParent array and process after this menuWithGrandParent.push(menu); } }); And our builtMenu should now look like this:\nAt this stage, our builtMenu should look like this:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 { \u0026#34;Main Menu\u0026#34;: [ { ...menu // \u0026lt;- Menu details }, { ...menu // \u0026lt;- Menu details }, { ...menu // \u0026lt;- Menu details }, { ...menu // \u0026lt;- Menu details }, { // Parent Menu ...menu, // \u0026lt;- Menu details children: [ { // Child Menu ...menu, // \u0026lt;- Menu details }, ] } ] } Parsing of Second Level menus and sorting Now that we have the parent and children out, we are left with just one level deep, which is for the grandchild. The code is similar to the one that generate the children menus just that we have to get the index for the parent, grandparent and ensure that they have children or we create one for them before we can append the new array into them.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 // Processes menu that has grandparent menuWithGrandParent.map(menu =\u0026gt; { // Get the parent of the parent let menuParent = menu.parent; // Split the parent to get the index of the parent and grand parent const indexSplit = menuParent.split(\u0026#34;.\u0026#34;); // Get the position of this menu const grandChildMenuPosition = builtMenu[menu.position]; // Get the index of the grand parent const grandParentIndex = indexSplit[0] - 1 \u0026lt; 0 ? 0 : indexSplit[0] - 1; // Set the index of the parent const parentIndex = indexSplit[1] - 1 \u0026lt; 0 ? 0 : indexSplit[1] - 1; // The menus at the position of the grand child menu using the grand parent index const grandParentMenu = grandChildMenuPosition[grandParentIndex]; // If this grand parent has children, which we expect that it should if (grandParentMenu.children) { // We get the parent of the menu from the grandparent\u0026#39;s children const parent = grandParentMenu.children[parentIndex]; // if the parent exists if (parent) { // Initialize children on the parent if it has none if (!parent.children) { parent.children = []; } // Push the menu to the parent under the grand parent parent.children.push(menu); // SOrt the children of the parent of the grandchild to maintain the ordering parent.children.sort((a, b) =\u0026gt; a.weight \u0026gt; b.weight); } } }); Finally, our builtMenu should look like this:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 { \u0026#34;Main Menu\u0026#34;: [ { ...menu // \u0026lt;- Menu details }, { ...menu // \u0026lt;- Menu details }, { ...menu // \u0026lt;- Menu details }, { ...menu // \u0026lt;- Menu details }, { // Grand Parent Menu ...menu, // \u0026lt;- Menu details children: [ { // Parent Menu ...menu, // \u0026lt;- Menu details ...menu, // \u0026lt;- Menu details children: [ { // Child Menu ...menu, // \u0026lt;- Menu details }, ] }, ] } ] } Putting it all together Compiling all together, the middleware looks like:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 async handle({ request, view }, next) { // call next to advance the request // Can be MongoDB Model, this is AdonisJS const MenuService = use(\u0026#34;App/Services/Admin/MenuService\u0026#34;); const menuService = new MenuService(); const menus = await menuService.findAll(\u0026#34;position\u0026#34;); const builtMenu = {}; const menuWithParent = []; // Stores all menu that has parent for later processing // First of all, gather all the menus with same position to the same index menus.map(menu =\u0026gt; { // The position for the current menu does not exist in the builtMenu object if (!builtMenu[menu.position]) { // Create it with an empty array builtMenu[menu.position] = []; } // If this menu does not have parent, push it directly into its position key in the builtMenu variable if (!menu.parent) { builtMenu[menu.position].push(menu); } else { // Else, push for later processing menuWithParent.push(menu); } }); const sortedMenu = {}; // Holds all the menu that has been sorted // Sorts all the menu in their respective positions, just to maintain ordering Object.keys(builtMenu).map(positionKey =\u0026gt; { const menuAtPosition = builtMenu[positionKey]; sortedMenu[positionKey] = menuAtPosition.sort( (a, b) =\u0026gt; a.weight \u0026gt; b.weight ); }); // This variable holds the menus that has grandparent const menuWithGrandParent = []; // We want to work on the menu that has parent menuWithParent.map(menu =\u0026gt; { // following the structure of our menu in the database, parentIndex is the value of the parent key of every menu that has parent let parentIndex = menu.parent; // We still have to determine if the menu is a grandchild of a parent, so we split by the dot separator that we have used then use the length of the array as a determinant const hasGrandChild = parentIndex.split(\u0026#34;.\u0026#34;).length \u0026gt; 1; // This menu is not a grandchild, we can proceed with our process if (!hasGrandChild) { // We need to get the position of the menu const menusAtPosition = builtMenu[menu.position]; parentIndex = parseInt(parentIndex); // Decrements the parentIndex to get the index in the array, in any case the reducing by 1 is lesser than zero, we want to default to index 0 else we want to get the index in the array const parent = menusAtPosition[parentIndex - 1 \u0026lt; 0 ? 0 : parentIndex - 1]; // If the parent does not have children, we to initialize that to an array if (!parent.children) { parent.children = []; } // Push the new child of the parent into its children parent.children.push(menu); // Sort the children in the parent to maintain ordering parent.children.sort((a, b) =\u0026gt; a.weight \u0026gt; b.weight); } else { // This menu has a grandparent, we can not process it like this, we push to the menuWithGrandParent array and process after this menuWithGrandParent.push(menu); } }); // Processes menu that has grandparent menuWithGrandParent.map(menu =\u0026gt; { // Get the parent of the parent let menuParent = menu.parent; // Split the parent to get the index of the parent and grand parent const indexSplit = menuParent.split(\u0026#34;.\u0026#34;); // Get the position of this menu const grandChildMenuPosition = builtMenu[menu.position]; // Get the index of the grand parent const grandParentIndex = indexSplit[0] - 1 \u0026lt; 0 ? 0 : indexSplit[0] - 1; // Set the index of the parent const parentIndex = indexSplit[1] - 1 \u0026lt; 0 ? 0 : indexSplit[1] - 1; // The menus at the position of the grand child menu using the grand parent index const grandParentMenu = grandChildMenuPosition[grandParentIndex]; // If this grand parent has children, which we expect that it should if (grandParentMenu.children) { // We get the parent of the menu from the grandparent\u0026#39;s children const parent = grandParentMenu.children[parentIndex]; // if the parent exists if (parent) { // Initialize children on the parent if it has none if (!parent.children) { parent.children = []; } // Push the menu to the parent under the grand parent parent.children.push(menu); // SOrt the children of the parent of the grandchild to maintain the ordering parent.children.sort((a, b) =\u0026gt; a.weight \u0026gt; b.weight); } } }); // At this point built menu contains keys of menu positions, the value of every keys is an array of nested values that can be passed to drag and drop UI libraries like JQuery nestable for admin configuration using a browser. // Adonis code to share values across all views. view.share({ siteMenus: builtMenu }); } Consuming the menu data from frontend The easiest way to consume the menu is to use a JQuery plugin called nestable++, the plugin make updating, creating and storing of the menu easier with the data that we have.\nIt expects an ordered list to be populated with list items and if children is expected, the list items should contain another unordered list with the children as the list items.\nIn our own case, once the view has set a JavaScript object with the menu data as its value, we can do something like this:\n1 2 3 4 5 6 7 8 9 10 11 12 let CURRENT_MENU = \u0026#34;Main Menu\u0026#34;; $(function() { function decodeHtml(html) { var txt = document.createElement(\u0026#34;textarea\u0026#34;); txt.innerHTML = html; return txt.value; } const siteMenus = JSON.parse(decodeHtml(window._siteMenus)); // This initializes the drag and drop nestable listing nestableMenuBuilder(siteMenus[CURRENT_MENU], { maxDepth: 3 }); }); The nestableMenuBuilder function looks like this:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 const nestableMenuBuilder = function(menuList, options = {}) { // Creates a single menu item that the nestable understands const menuItem = function(item) { return $(` \u0026lt;li class=\u0026#34;dd-item\u0026#34; data-id=\u0026#34;${item.id}\u0026#34; data-name=\u0026#34;${ item.name }\u0026#34; data-slug=\u0026#34;${item.url}\u0026#34; data-new=\u0026#34;${item.new || 0}\u0026#34; data-deleted=\u0026#34;0\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;dd-handle\u0026#34;\u0026gt;${item.name}\u0026lt;/div\u0026gt; \u0026lt;span class=\u0026#34;button-delete btn btn-default btn-xs pull-right\u0026#34; data-owner-id=\u0026#34;${item.id}\u0026#34;\u0026gt; \u0026lt;i class=\u0026#34;nestable-icon\u0026#34; data-feather=\u0026#34;x\u0026#34; \u0026gt;\u0026lt;/i\u0026gt; \u0026lt;/span\u0026gt; \u0026lt;span class=\u0026#34;button-edit btn btn-default btn-xs pull-right\u0026#34; data-owner-id=\u0026#34;${item.id}\u0026#34;\u0026gt; \u0026lt;i class=\u0026#34;nestable-icon\u0026#34; data-feather=\u0026#34;edit-3\u0026#34;\u0026gt;\u0026lt;/i\u0026gt; \u0026lt;/span\u0026gt; \u0026lt;/li\u0026gt;`); }; const buildNestable = function(items) { const $parent = $(\u0026#39;\u0026lt;ol class=\u0026#34;dd-list\u0026#34;\u0026gt;\u0026lt;/ul\u0026gt;\u0026#39;); const createChildren = function(items, $_parent) { return items.map(item =\u0026gt; { const $_el = menuItem(item); if (item.children) { const $_parent = $(\u0026#39;\u0026lt;ol class=\u0026#34;dd-list\u0026#34;\u0026gt;\u0026lt;/ul\u0026gt;\u0026#39;); createChildren(item.children, $_parent); $_el.append($_parent); } $_parent.append($_el); }); }; items.map(item =\u0026gt; { const $_el = menuItem(item); if (item.children) { const $_parent = $(\u0026#39;\u0026lt;ol class=\u0026#34;dd-list\u0026#34;\u0026gt;\u0026lt;/ul\u0026gt;\u0026#39;); createChildren(item.children, $_parent); $_el.append($_parent); } $parent.append($_el); }); return $parent; }; const nestableList = buildNestable(menuList); // Initializes nestable++ $(\u0026#34;.dd.nestable\u0026#34;) .empty() .append(nestableList); }; In our html, we have to include the nestable++ plugin from here and its css from here and JQuery, then create an ol with class names dd nestable.\n1 \u0026lt;ol class=\u0026#34;dd nestable\u0026#34;\u0026gt;\u0026lt;/ol\u0026gt; And, that is it.\nConclusion There are other features of nestable that I do not include here, like updating, adding new item and deleting a menu item. I feel those are for nestable++ itself, including them here would take us out of scope for the post.\n","permalink":"https://limistah.dev/posts/wordpress-like-menu-node-apps/","summary":"\u003cp\u003eYou might have been in this kind of trap before or currently in one, well, I just want to tell you that I know your pain.\u003c/p\u003e\n\u003cp\u003eIn a custom built CMS, managing of Menu and navigation in the site from the Admin Dashboard is a requirement. While it is interesting to use, it is not as interesting to build.\u003c/p\u003e\n\u003cp\u003eI just walked past this process, here, I am sharing how I have conquered it.\u003c/p\u003e","title":"Menu system in Node Apps like WordPress Menu"},{"content":"Beginners in ReactJS often face this kind of error: It is not just with inputs, it is with all HTML elements that does not expect a closing tags, they are called empty elements. A list of these tags could be found here.\nTo solve this very easily, we just have to follow the HTML semantics by ending all empty elements with /\u0026gt; instead of \u0026gt;\nSo inputs should look like this:\n1 \u0026lt;input type=\u0026#34;text\u0026#34; /\u0026gt; For image\n1 \u0026lt;img href=\u0026#34;text\u0026#34; alt=\u0026#34;profile avatar\u0026#34; /\u0026gt; Just note that:\nIn HTML the ending slash in optional, in ReactJS applications, it is required\nAnd likes\u0026hellip;.\nI hope this saves someone some nagging and head nuts.\n","permalink":"https://limistah.dev/posts/react-unexpected-closing-tag/","summary":"\u003cp\u003eBeginners in ReactJS often face this kind of error:\n\u003cimg alt=\"React JS error\" loading=\"lazy\" src=\"/assets/reactjs-tag-error.png\"\u003e\u003c/p\u003e\n\u003cp\u003eIt is not just with inputs, it is with all HTML elements that does not expect a closing tags, they are called empty elements. A list of these tags could be found \u003ca \n  href=\"http://xahlee.info/js/html5_non-closing_tag.html\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003ehere\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eTo solve this very easily, we just have to follow the HTML semantics by ending all empty elements with \u003ccode\u003e/\u0026gt;\u003c/code\u003e instead of \u003ccode\u003e\u0026gt;\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003eSo inputs should look like this:\u003c/p\u003e","title":"ReactJS - Unexpected closing tag"},{"content":"Last time, I was trying to render a data table and I thought that I should make some fields editable right in the table listing. It is interesting to note that I never thought about any NPM library for it, so I went all out to create a simple but effective solution for myself.\nIn this post, I will demonstrate how I created an editable component. The component would be able to use different form fields and notify the parent if any change has been made. That said, Let\u0026rsquo;s move\u0026hellip;.\nTo start with, I will define the usage that I had for the component. The component to be was consumed like this:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 \u0026lt;InplaceEdit isUpdatingId={true || false} id={idForTheField || \u0026#34;\u0026#34;} updateKey={keyToReturnAnObjectWith || \u0026#34;\u0026#34;} onUpdate={functionToCallForTheUpdate || () =\u0026gt; {}} text={textForDisplay || \u0026#34;\u0026#34;} defaultValue={defaultInCaseValueDoesNotMatch || \u0026#34;\u0026#34;} inputType={htmlInputType || \u0026#34;text\u0026#34;} selectOptions={ optionForSelectInputType || [ { value: true, label: \u0026#34;Yes\u0026#34; }, { value: false, label: \u0026#34;No\u0026#34; } ]} valueToText={ functionToConvertFieldValueToText || value =\u0026gt; (value === \u0026#34;true\u0026#34; ? \u0026#34;Yes\u0026#34; : \u0026#34;No\u0026#34;)} /\u0026gt;; With this, I was able to use the InplaceEdit component almost anywhere that I wanted as far as single value was expected.\nNow, building for the usage. Firstly, I defined the InplaceEdit component:\n1 2 3 4 5 6 7 8 9 10 11 import React from \u0026#34;react\u0026#34; // I won\u0026#39;t repeat this after now function InplaceEdit() { return ( \u0026lt;div\u0026gt; Inplace Edit Component \u0026lt;/div\u0026gt; ); } export default InplaceEdit; // I won\u0026#39;t repeat this after now Next, I wanted to toggle between two modes. One being a normal text, and the other when it is a form field. To do this, I added an onClick handler to the root div element, which updates a state that determines these modes.\n1 2 3 4 5 6 7 8 9 10 11 12 13 function InplaceEdit() { const [showEditor, updateShowEditor] = React.useState(false); const handleShowEditor = () =\u0026gt; { updateShowEditor(true); }; return ( \u0026lt;div onClick={handleShowEditor} style={{ cursor: \u0026#34;pointer\u0026#34; }}\u0026gt; {showEditor ? \u0026#34;Show Editor\u0026#34; : \u0026#34;Show Text\u0026#34;} \u0026lt;/div\u0026gt; ); } Next, I required two distinct components for both the editor and display, I called them EditDisplay and TextDisplay, respectively. This made the InplaceEdit component looked more like this:\n1 2 3 4 5 6 7 8 9 10 11 12 13 function InplaceEdit() { const [showEditor, updateShowEditor] = React.useState(false); const handleShowEditor = () =\u0026gt; { updateShowEditor(true); }; return ( \u0026lt;div onClick={handleShowEditor} style={{ cursor: \u0026#34;pointer\u0026#34; }}\u0026gt; {showEditor ? \u0026lt;EditDisplay/\u0026gt; : \u0026lt;TextDisplay /\u0026gt;} \u0026lt;/div\u0026gt; ); } After that, I defined the EditDisplay component and TextDisplay components:\n1 2 3 4 5 6 7 8 9 // EditDisplay Component function EditDisplay() { return ( \u0026lt;\u0026gt;\u0026lt;/\u0026gt; ); } // Text display component const TextDisplay = () =\u0026gt; \u0026lt;\u0026gt;\u0026lt;/\u0026gt;; So, at this point, I was able to toggle between two modes, but nothing happens and nothing is rendered, yet. To move forward, I had to display the text which would be received by the InplaceEdit component and passed to the TextDisplay component as props. The below code is what I implemented for this phase:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 // EditDisplay Component function EditDisplay() { return ( \u0026lt;\u0026gt;\u0026lt;/\u0026gt; ); } // Text display component const TextDisplay = ({text}) =\u0026gt; \u0026lt;span\u0026gt;{text}\u0026lt;/span\u0026gt;; function InplaceEdit({ text }) { const [showEditor, updateShowEditor] = React.useState(false); const handleShowEditor = () =\u0026gt; { updateShowEditor(true); }; return ( \u0026lt;div onClick={handleShowEditor} style={{ cursor: \u0026#34;pointer\u0026#34; }}\u0026gt; {showEditor ? \u0026lt;EditDisplay/\u0026gt; : \u0026lt;TextDisplay text={text} /\u0026gt;} \u0026lt;/div\u0026gt; ); } I was glad when I got to this point. I am sure you are as well, at least, something is getting rendered.\nIt is very correct if you guessed that the TextDisplay component will not change its state afterwards, and bulk of the work is around the EditDisplay component. So, sorry, I won\u0026rsquo;t be adding that style that you are thinking.\nThe next move I made was to tell the EditDisplay what type of edit is required and the default value for the edit.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 // EditDisplay Component function EditDisplay({ defaultValue, inputType }) { return \u0026lt;\u0026gt;\u0026lt;/\u0026gt; } // Text display component const TextDisplay = ({ text }) =\u0026gt; \u0026lt;span\u0026gt;{text}\u0026lt;/span\u0026gt; function InplaceEdit({ text, inputType, defaultValue }) { const [showEditor, updateShowEditor] = React.useState(false) const handleShowEditor = () =\u0026gt; { updateShowEditor(true) } return ( \u0026lt;div onClick={handleShowEditor} style={{ cursor: \u0026#34;pointer\u0026#34; }}\u0026gt; {showEditor ? ( \u0026lt;EditDisplay inputType={inputType || \u0026#34;text\u0026#34;} defaultValue={defaultValue} /\u0026gt; ) : ( \u0026lt;TextDisplay text={text} /\u0026gt; )} \u0026lt;/div\u0026gt; ) } The defaultValue and inputType are both passed from the InplaceEdit component and passed down to the EditDisplay component, the inputType is short-circuited to return text in case the defaultValue is not set. Not that bad, right?\nThe next step is that, I put up some input components for the update. I had TextInput which looked like this:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const TextInput = ({ type, defaultValue, onBlur, disabled }) =\u0026gt; { const handleKeyUp = e =\u0026gt; { if (e.keyCode === 13) { onBlur(e); } }; const inputRef = React.useRef(); React.useEffect(() =\u0026gt; { inputRef.current.focus(); }, [inputRef]); return ( \u0026lt;input ref={inputRef} type={type} defaultValue={defaultValue} onBlur={onBlur} disabled={disabled} onKeyUp={handleKeyUp} /\u0026gt; ); }; This componentTextInput does a pretty interesting stuff, first, it handles the KeyUp event which sends information up to the parent(InplaceEdit) component, it can be disabled, the type is set as from the parent and the defaultValue is also configurable through the props.\nAnd the SelectInput, I defined it to look like this:\n1 2 3 4 5 6 7 8 9 const SelectInput = ({ options, defaultValue, onBlur, disabled }) =\u0026gt; { return ( \u0026lt;select onChange={onBlur} defaultValue={defaultValue} disabled={disabled}\u0026gt; {options.map(option =\u0026gt; { return \u0026lt;option value={option.value}\u0026gt;{option.label}\u0026lt;/option\u0026gt;; })} \u0026lt;/select\u0026gt; ); }; This component(SelectInput) accepts the options as an array of object with label and value keys, defaultValue for persistence, onBlur event handler and can be disabled by setting its disabled props. Not too serious, I think.\nFor the EditDisplay, I had to render one of the two types of components based on the inputType props:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 const EditDisplay = ({ inputType, onchange, defaultValue, selectOptions, onBlur, disabled }) =\u0026gt; { return ( \u0026lt;\u0026gt; {inputType === \u0026#34;select\u0026#34; \u0026amp;\u0026amp; ( \u0026lt;SelectInput options={selectOptions} defaultValue={defaultValue} onchange={onchange} onBlur={onBlur} disabled={disabled} /\u0026gt; )} {[\u0026#34;text\u0026#34;, \u0026#34;password\u0026#34;].includes(inputType) \u0026amp;\u0026amp; ( \u0026lt;TextInput type={inputType} onchange={onchange} /\u0026gt; )} \u0026lt;/\u0026gt; ); }; Next, I thought, the EditDisplay component receives some props that are not yet defined in the InplaceEdit component, so I defined them like so:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 function InplaceEdit({ text, inputType, defaultValue, valueToText, updateKey, onUpdate, }) { const [showEditor, updateShowEditor] = React.useState(false) // Keeps the text synchronized const [displayText, updateDisplayText] = React.useState(text) const handleShowEditor = () =\u0026gt; { updateShowEditor(true) } // Callback for the onBlur event handler const handleEditorInputChange = React.useCallback(e =\u0026gt; { const value = e.currentTarget.value // Converts the value to a displayable text using a function from the parent consumer const text = typeof valueToText === \u0026#34;function\u0026#34; ? valueToText(value) : value // Updates the display text for synchronization updateDisplayText(text) // Hides the editor updateShowEditor(false) if (defaultValue !== value) { // If the value has changed, we need to tell the consumer parents about it const handler = typeof onUpdate === \u0026#34;function\u0026#34; ? onUpdate : () =\u0026gt; {} // Sets the data returning an object containing the value in the updateKey or just the value itself const data = updateKey ? { [updateKey]: value } : value // Call the update function with the data and resource id handler(data, id) } }) return ( \u0026lt;\u0026gt; \u0026lt;div onClick={handleShowEditor} style={{ cursor: \u0026#34;pointer\u0026#34; }}\u0026gt; {showEditor ? ( \u0026lt;EditDisplay inputType={inputType || \u0026#34;text\u0026#34;} defaultValue={defaultValue} onBlur={handleEditorInputChange} selectOptions={selectOptions} disabled={isUpdatingId === id} /\u0026gt; ) : ( \u0026lt;TextDisplay text={displayText} /\u0026gt; )} \u0026lt;/div\u0026gt; \u0026lt;/\u0026gt; ) } Noticeable from the above snippet is that, the disabled prop is only set if the isUpdatingId is equals to the id props, and the \u0026lt;TextDisplay /\u0026gt; now receives a displayText state which is synchronized across the two children of InplaceEdit component.\nAnd guess what, that is all there is to an InplaceEdit component that returns just a single value. It should accept a value/text and toggles between input and text states, too damn simple, I think.\nConclusion This was a very interesting process for me, and I never regret it and never will. There are awesome React components for this and does better job than my local component. But I thougth, I should experiment a bit, and here I have it.\nThe full code is available as a gist here, if you want to check it out. And, thank you for reading.\nDhanyavaad! 🙇\n","permalink":"https://limistah.dev/posts/reactjs-inplace-edit-component/","summary":"\u003cp\u003eLast time, I was trying to render a data table and I thought that I should make some fields \u003cem\u003eeditable\u003c/em\u003e right in the table listing. It is interesting to note that I never thought about any \u003ca \n  href=\"https://www.npmjs.com/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eNPM\u003c/a\u003e library for it, so I went all out to create a simple but \u003ca \n  href=\"https://gist.github.com/limistah/b91b5429c0a4f8062b26445d12877361\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eeffective solution for myself\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eIn this post, I will demonstrate how I created an editable component. The component would be able to use different form fields and notify the parent if any change has been made. That said, Let\u0026rsquo;s move\u0026hellip;.\u003c/p\u003e","title":"ReactJS - Inplace Edit component"},{"content":"Modern application development requires that some actions are carried out when a point of the application is reached.\nTasks like confirmation email, invoice generation, logging and profiling are few of things that requires to be carried out in specific regions of application flow.\nThese actions that are triggered are called Events. Events in modern application development make code execution after a web request to the server has been completed to be possible.\nIn this post, I will demonstrate how to implement events in AdonisJS.\nFirstly, from the terminal, in an AdonisJS application directory, execute this command:\n1 adonis make: listener NewUser This should show an output similar to:\n1 ✔ create app/Listeners/NewUser.js Indicating that a file has been created at app/Listeners/NewUser.js. Right!\nNext, we need to define what happens when this event is called. We would go into the newly generated file and write some code into it. In my case, I would just log he details of the new user.\n1 2 3 4 5 6 7 \u0026#39;use strict\u0026#39; const NewUser = exports = module.exports = {} NewUser.registered = async (user) =\u0026gt; { console.log(user) } At this point, we need to move forward to tell AdonisJS about our listener. We can do this by going to start/events.js, sometimes the file does not exist, you are free to create yourself.\nInside of start/events.js I have:\n1 2 3 const Event = use(\u0026#34;Event\u0026#34;); Event.on(\u0026#34;NewUser::registered\u0026#34;, \u0026#34;NewUser.registered\u0026#34;); The above code loads the Event from the IoC, then I called the .on method to register an event named NewUser::registered, then determines the listener and method to call when the event is invoked. In this case, I want NewUser.registered to be executed when this event is triggered.\nAwesome!!!\nAnd we are done with setting up an event in AdonisJS. But we have not called this event or see it in action. How can we do that.\nFrom anywhere in your application call:\n1 2 3 4 5 6 7 const Event = use(\u0026#34;Event\u0026#34;); Event.fire( \u0026#34;NewUser::registered\u0026#34;, user ); With the above code, I pulled Event from IoC, then call .fire on it with the name of the event that I want to execute as the first parameter and the parameters that the event handler(s) would receive are the arguments listed after the event name, in this case, only user is sent to NewUser.registered\nAnd that is it.\nEvents in an AdonisJS application.\nThis tiny feature is too useful, don\u0026rsquo;t render it useless.\nShalom! ☮️\n","permalink":"https://limistah.dev/posts/adonisjs-use-events/","summary":"\u003cp\u003eModern application development requires that some actions are carried out when a point of the application is reached.\u003c/p\u003e\n\u003cp\u003eTasks like confirmation email, invoice generation, logging and profiling are few of things that requires to be carried out in specific regions of application flow.\u003c/p\u003e\n\u003cp\u003eThese actions that are triggered are called Events. Events in modern application development make code execution after a web request to the server has been completed to be possible.\u003c/p\u003e","title":"AdonisJS - Event"},{"content":"AdonisJS was built for the NodeJS Artisans taking after the concepts of Laravel - The PHP framework for Artisans. AdonisJS did a great job porting these concepts into JavaScript, it maintains the namespace even though JavaScript does not support that, it using its own fast, easy and extendable view engine and many more, but some features of Laravel are not shipped with AdonisJS by default.\nThe IoC container in Laravel auto injects classes by inspection when a recognized namespace is Type Hinted. This makes route model binding easier with Laravel. In JavaScript, there is little that can be done to achieve this, so, there is a need for a custom approach to this.\nWhile I believe you have installed AdonisJS and have created an application with the adonis command, we will begin by creating a resource route:\n1 2 3 4 Route.resource( \u0026#34;users\u0026#34;, \u0026#34;UserController\u0026#34; ).only(\u0026#34;update\u0026#34;); Next we can make a controller for the User from the terminal using:\na d o n i s m a k e : c o n t r o l l e r U s e r C o n t r o l l e r - r e s o u r c e The above command will create a file /app/Controllers/UserController.js with predefined methods for a cruddy endpoint. Awesome!\nAnd in the generated controller, lets return the id as JSON\nu } p d r / a e a t t p e u p r / ( n C { o r r n e e t q s r u p o e o l s n l t s e , e r . s r j / e s U s o s p n e o ( r n { C s u o e s n } e t ) r r I o { d l : l e r r e . q j u s e s t . i d } ) ; Now, when we call PUT /users/1, it should return this:\n1 2 3 { \u0026#34;userId\u0026#34;: 1 } Interesting\u0026hellip;\nTraditionally, we would have to lookup the id against the User Model, but, we would be doing this everytime for every route and that\u0026rsquo;s absurd.\nHow sweet could it be to have a middleware that does that by default, such that we can just call the middleware like so:\n1 2 3 Route.resource(\u0026#34;users\u0026#34;, \u0026#34;UserController\u0026#34;) .only(\u0026#34;update\u0026#34;) .middleware(new Map([[[\u0026#34;update\u0026#34;, \u0026#34;boundRouteModel:App/Model/User,user\u0026#34;]]])); Awesome right?\nLet\u0026rsquo;s do this:\nFirstly, let\u0026rsquo;s make a new middleware:\na d o n i s m a k e : m i d d l e w a r e B o u n d R o u t e M o d e l - r e s o u r c e And register it in the /start/kernel.js, and add the declaration to the nameMiddleware looks this:\n1 2 3 4 5 6 7 8 // /start/kernel.js // ...... const namedMiddleware: { //... \u0026#34;boundRouteModel\u0026#34;: \u0026#34;App/Middleware/BoundRouteModelMiddleware\u0026#34; } // ...... Awesome!\nNext in the /app/Middleware/BoundRouteModelMiddleware.js, replace the handle function with this piece of code.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 async handle({ request }, next, [model, identifier, lookupField = \u0026#34;id\u0026#34;]) { if (typeof model === \u0026#34;string\u0026#34;) { // Use model directly if a string model = use(model); } else if (Array.isArray(model)) { // Maps through if an array model = model.map(async _model =\u0026gt; { // Model is a string, use directly if (typeof _model === \u0026#34;string\u0026#34;) { return use(_model); } else if ( _model.findByOrFail \u0026amp;\u0026amp; typeof _model.findByOrFail === \u0026#34;function\u0026#34; ) { // Model already contains an already imported model return _model; } // _model is not recognized by this code as usable or callable for its usage return null; }); } else if (model \u0026amp;\u0026amp; !model.$booted) { // model is defined from the middleware arguments but not actually a Model await next(); } if (model \u0026amp;\u0026amp; typeof model !== \u0026#34;string\u0026#34;) { const params = request.params; Object.keys(params).map(async (paramKey, index) =\u0026gt; { let modelForParam = model; // Get the model at the index if the model is an array if (Array.isArray(model)) { modelForParam = model[index]; } if (modelForParam \u0026amp;\u0026amp; modelForParam.$booted) { const paramValue = params[paramKey]; request[identifier] = await modelForParam.findByOrFail( lookupField, // The field to use for the lookup paramValue // Value passed from the request ); } }); } // call next to advance the request await next(); } The code above is doing pretty lot of things.\nFirst it assumes that the model passed to the middleware is a string.\nThen it attemps to import it from the IoC container, but if the model is an array, it loops through the array checking if the element is a string and tries to import it, or use it directly if it is an alreay imported Model.\nBut if the model is defined and is not recognized as a Model or an Array, the middleware is passed on to the next.\nFinally, the actual look up starts by checking if the model is not a string, picks up every item from the request parameter and assign a model at the index of the request parameter to it if model is an array, else if the model is a Model, it just looks it up and stores the result in the request using the specified identifier in the middleware initialization.\nYes, that is all to it.\nWhen next you want to use route model binding, you just use the middleware.\nConclusion It might feel like a heck of a task, but to me it is worth it. There is a library for this purpose and it uses the provider technique, you might want to check it out if you don\u0026rsquo;t like this approach.\nThanks for reading. 🙇🙇🙇\n","permalink":"https://limistah.dev/posts/adonisjs-route-model-binding/","summary":"\u003cp\u003e\u003ca \n  href=\"https://adonisjs.com/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eAdonisJS\u003c/a\u003e was built for the NodeJS Artisans taking after the concepts of \u003ca \n  href=\"https://laravel.com\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eLaravel - The PHP framework for Artisans\u003c/a\u003e. AdonisJS did a great job porting these concepts into JavaScript, it maintains the namespace even though JavaScript does not support that, it using its own fast, easy and extendable view engine and many more, but some features of Laravel are not shipped with AdonisJS by default.\u003c/p\u003e\n\u003cp\u003eThe \u003ca \n  href=\"https://laravel.com/docs/4.2/ioc\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eIoC container in Laravel\u003c/a\u003e auto injects classes by inspection when a recognized namespace is \u003ca \n  href=\"https://www.php.net/manual/en/language.oop5.typehinting.php\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003e\u003cem\u003eType Hinted\u003c/em\u003e\u003c/a\u003e. This makes route model binding easier with Laravel. In JavaScript, there is little that can be done to achieve this, so, there is a need for a custom approach to this.\u003c/p\u003e","title":"AdonisJS - Route Model Binding"},{"content":"First, install the Validator using the adonis command:\n1 adonis install @adonisjs/validtor Create a resource route\n1 adonis make:controller PostController --resource Define the route in start/route.js\n1 2 3 //.. Route.resource(\u0026#34;posts\u0026#34;, \u0026#34;PostController\u0026#34;) //.. Now, we can make a Validator for /posts/store\na d o n i s m a k e : v a l i d a t o r S t o r e P o s t This will create a validator in /app/Validators.\nFinally, To define a validator for a specific route in the definition, do something like this:\n1 2 3 //.. Route.resource(\u0026#34;posts\u0026#34;, \u0026#34;PostController\u0026#34;).validator([[[\u0026#34;store\u0026#34;, \u0026#34;StoreUser\u0026#34;]]]) //.. PSSS: I want this as short as it can while still answering question regarding the final code. 🥂\n","permalink":"https://limistah.dev/posts/adonisjs-validators-with-resource-routes/","summary":"\u003cp\u003eFirst, install the Validator using the \u003ccode\u003eadonis\u003c/code\u003e command:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-0-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-1\"\u003e1\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eadonis install @adonisjs/validtor\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003eCreate a resource route\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-1-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-1-1\"\u003e1\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eadonis make:controller PostController --resource\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003eDefine the route in \u003ccode\u003estart/route.js\u003c/code\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-2-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-1\"\u003e1\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-2\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-2\"\u003e2\u003c/a\u003e\n\u003c/span\u003e\u003cspan class=\"lnt\" id=\"hl-2-3\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-2-3\"\u003e3\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-js\" data-lang=\"js\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e//..\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e\u003c/span\u003e\u003cspan class=\"nx\"\u003eRoute\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eresource\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;posts\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;PostController\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e//..\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003eNow, we can make a \u003ccode\u003eValidator\u003c/code\u003e for \u003ccode\u003e/posts/store\u003c/code\u003e\u003c/p\u003e\n\n\n\n\u003cdiv class=\"goat svg-container \"\u003e\n  \n    \u003csvg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      font-family=\"Menlo,Lucida Console,monospace\"\n      \n        viewBox=\"0 0 256 25\"\n      \u003e\n      \u003cg transform='translate(8,16)'\u003e\n\u003ctext text-anchor='middle' x='0' y='4' fill='currentColor' style='font-size:1em'\u003ea\u003c/text\u003e\n\u003ctext text-anchor='middle' x='8' y='4' fill='currentColor' style='font-size:1em'\u003ed\u003c/text\u003e\n\u003ctext text-anchor='middle' x='16' y='4' fill='currentColor' style='font-size:1em'\u003eo\u003c/text\u003e\n\u003ctext text-anchor='middle' x='24' y='4' fill='currentColor' style='font-size:1em'\u003en\u003c/text\u003e\n\u003ctext text-anchor='middle' x='32' y='4' fill='currentColor' style='font-size:1em'\u003ei\u003c/text\u003e\n\u003ctext text-anchor='middle' x='40' y='4' fill='currentColor' style='font-size:1em'\u003es\u003c/text\u003e\n\u003ctext text-anchor='middle' x='56' y='4' fill='currentColor' style='font-size:1em'\u003em\u003c/text\u003e\n\u003ctext text-anchor='middle' x='64' y='4' fill='currentColor' style='font-size:1em'\u003ea\u003c/text\u003e\n\u003ctext text-anchor='middle' x='72' y='4' fill='currentColor' style='font-size:1em'\u003ek\u003c/text\u003e\n\u003ctext text-anchor='middle' x='80' y='4' fill='currentColor' style='font-size:1em'\u003ee\u003c/text\u003e\n\u003ctext text-anchor='middle' x='88' y='4' fill='currentColor' style='font-size:1em'\u003e:\u003c/text\u003e\n\u003ctext text-anchor='middle' x='96' y='4' fill='currentColor' style='font-size:1em'\u003ev\u003c/text\u003e\n\u003ctext text-anchor='middle' x='104' y='4' fill='currentColor' style='font-size:1em'\u003ea\u003c/text\u003e\n\u003ctext text-anchor='middle' x='112' y='4' fill='currentColor' style='font-size:1em'\u003el\u003c/text\u003e\n\u003ctext text-anchor='middle' x='120' y='4' fill='currentColor' style='font-size:1em'\u003ei\u003c/text\u003e\n\u003ctext text-anchor='middle' x='128' y='4' fill='currentColor' style='font-size:1em'\u003ed\u003c/text\u003e\n\u003ctext text-anchor='middle' x='136' y='4' fill='currentColor' style='font-size:1em'\u003ea\u003c/text\u003e\n\u003ctext text-anchor='middle' x='144' y='4' fill='currentColor' style='font-size:1em'\u003et\u003c/text\u003e\n\u003ctext text-anchor='middle' x='152' y='4' fill='currentColor' style='font-size:1em'\u003eo\u003c/text\u003e\n\u003ctext text-anchor='middle' x='160' y='4' fill='currentColor' style='font-size:1em'\u003er\u003c/text\u003e\n\u003ctext text-anchor='middle' x='176' y='4' fill='currentColor' style='font-size:1em'\u003eS\u003c/text\u003e\n\u003ctext text-anchor='middle' x='184' y='4' fill='currentColor' style='font-size:1em'\u003et\u003c/text\u003e\n\u003ctext text-anchor='middle' x='192' y='4' fill='currentColor' style='font-size:1em'\u003eo\u003c/text\u003e\n\u003ctext text-anchor='middle' x='200' y='4' fill='currentColor' style='font-size:1em'\u003er\u003c/text\u003e\n\u003ctext text-anchor='middle' x='208' y='4' fill='currentColor' style='font-size:1em'\u003ee\u003c/text\u003e\n\u003ctext text-anchor='middle' x='216' y='4' fill='currentColor' style='font-size:1em'\u003eP\u003c/text\u003e\n\u003ctext text-anchor='middle' x='224' y='4' fill='currentColor' style='font-size:1em'\u003eo\u003c/text\u003e\n\u003ctext text-anchor='middle' x='232' y='4' fill='currentColor' style='font-size:1em'\u003es\u003c/text\u003e\n\u003ctext text-anchor='middle' x='240' y='4' fill='currentColor' style='font-size:1em'\u003et\u003c/text\u003e\n\u003c/g\u003e\n\n    \u003c/svg\u003e\n  \n\u003c/div\u003e\n\u003cp\u003eThis will create a validator in \u003ccode\u003e/app/Validators\u003c/code\u003e.\u003c/p\u003e","title":"AdonisJS - Using Validators with resource routes"},{"content":"Yes, this topic is worth a post and I will walk you through why the migration was inevitable, and how I had done it.\nFormerly, this website used to run on WordPress, the great PHP CMS. The design was made with the aid of Typography theme, I loved it!\nAll of the post prior to this date was written using the WordPress admin dashboard. I loved the Gutenberg Editor, it was so fluid and easy to work with. Overall my experience was great.\nThe Scenario Sometimes, installing software updates as end user could daunt the total experience of the user, in a case where the most beloved feature could be stripped or the presence of a bug somewhere which could be shipped unintentionally with the update. My own case was none of these, my experience was a total breakdown of the site, and I did not know why.\nFortunately, I trust so much that when installing WordPress on my hosting server, I opted for auto install new updates. This particular feature is a serial killer, and has not killed just this particular website twice, it also killed another website of mine too.\nHow it happened On a fateful day, I got a message from one of my readers stating that my website is down, I felt it was something not really serious, and as a techy person, I could fix it on time. I went into the WordPress admin dashboard and it was looking all well. I looked at everywhere including the logs, I found no clue on what I could do to make it right. It was an embarrassment to my personality (a tech guy).\nI got depressed and worried, this meant that I could not serve my website\u0026rsquo;s content anymore, even though they were somewhere in the cloud? I sought for a solution and I remembered, Gatsby is a very cool guy to deal with.\nAfter a lot of deliberations, and giving my mind the needed time and space, I finally concluded, I would gain a whole lot using Gatsby as a platform and framework than being on WordPress. Few of my reasons were:\nIt is blazingly fast! It was written with JavaScript (My first love!) It gives a full control and total customization Free hosting through Netlify The Path Learning Gatsby Installing Gatsby and choosing a theme Customizing the look. Device testing Pulling posts from the old WordPress setup Hosting the website Learning Gatsby Firstly, I had to learn Gatsby through its very resourceful documentation. This was easier than I had thought, and the features they provide enshrined my belief that it is the most modern static site generator, so far.\nIt has far new, but beautiful concepts for its routing, content fetching and asset management. All of these, I thought have been implemented with every level of user\u0026rsquo;s expertise in mind.\nWith Gatsby, you can go from writing zero codes by using themes which still give you the level of customization the framework provides, or by using a starter to emphasize your genius.\nI walked through the less technical path, I picked a theme that best suit my need.\nInstalling Gatsby and choosing a theme Upon ensuring that I have substantial knowledge of Gatsby, I moved to initialize a new project for my website and choosing a theme I know would be good for me. This process took me a week of love and hate process.\nJoke apart, I tried doing stuffs the genius way. Yes, many blogs and websites that run on Gatsby had done that, I thought, I would enjoy greater level of customization than using a theme, I was totally wrong.\nAfter weeks of trying to be Alan Turing, I dropped down my hat to that level of novice and succumb to use a theme. I was all around looking for a nice looking theme that I could use, then I stumbled upon Theme UI Blog Theme, which answers all of my questions.\nFortunately for me, Gatsby itself has a starter that is based this beautiful UI library, so I opted for it.\nSo starting with the installation of Gatsby itself using npm, and ensuring that it is installed as a global package:\n1 npm install -g gatsby-cli This will install gatsby and make gatsby cli command available. Running gatsby --help should yield:\nThat\u0026rsquo;s one step forward.\nI later initialized a blog using the command below:\n1 gatsby new aleem.blog https://github.com/gatsbyjs/gatsby-starter-blog-theme The above command creates a new Gatsby website in aleem.blog directory, and also ensures that the starter that I would be using has integration of Theme UI\u0026rsquo;s integration. This simplifies my bootstrap process, still, I have the level of customization that I can ever think of with Gatsby using this starter.\nAfter a successful installation of the new website, the below command will run Gatsby in development mode:\n1 gatsby develop The result should be something like this:\nCustomizing the theme\u0026rsquo;s look and feel I love shades of purple, the theme comes with that by default, so I have no problem with the color at all. But, I do not like the way the page was structured, so I went to look deeper into the theme.\nIt happens that the theme expect that some files should be placed in a particular directory to override the default display.\nHere is a list of the files the theme uses to render its layout:\nFilename Location Purpose Bio Content src/gatsby-theme-blog/components/bio-content.js Author Biography Bio src/gatsby-theme-blog/components/bio.js Parent and data provider for Bio Content Footer src/gatsby-theme-blog/components/footer.js Posts page footer Header src/gatsby-theme-blog/components/footer.js Website Header Layout src/gatsby-theme-blog/components/layout.js Website General Layout Post Footer src/gatsby-theme-blog/components/post-footer.js Footer for the post page Post src/gatsby-theme-blog/components/post.js Single Post display renderer Posts src/gatsby-theme-blog/components/posts.js All Posts listing SEO src/gatsby-theme-blog/components/seo.js SEO utility Switch src/gatsby-theme-blog/components/switch.js Dark Mode Switch Color src/gatsby-theme-blog/gatsby-plugin-theme-ui/color.js Color configuration Typography src/gatsby-theme-blog/gatsby-plugin-theme-ui/typography.js Theme UI Typography As seen above, all these are what renders our new site. I took enough time to work on each file to give me what I wanted.\nThe posts are expected to live at content/posts, I maintained that. The header is from Theme UI Recipes, the homepage is just a vertical grid, nothing complicated.\nDevice testing Considering the various users that would stumble on the site, I ensure that while adding my customizations, I was checking for responsiveness all round. But that was not enough. I took time to get across screen sizes to see how it really look and feel.\nNot until I found the fluidness, I tweaked until I got something like this:\nPulling posts from the old WordPress setup Fulfilling all righteousness, I did not write any code to pull the posts from WordPress. I did the posts migration manually, through copying and pasting.\nKnowing when to write a program to simplify a task, and actually doing the task manually is a wisdom of its own. I had a few posts(less than 10), and I thought it would be best I do that manually which did save me some time from testing and debugging if I had written a program/library for it.\nNonetheless, if I would write a library to simplify the task, this is how I would do it:\nExport all posts from WordPress using their export tool. Get a suitable XML to HTML parser library on NPM. Get a suitable HTML to MD library on NPM. Parse the exported XML to HTML then to MD before saving it to the disk with each file named with the title of a post and containing the content of the post. The above does not take care of assets like images and media. I faced an issue with asset referencing while pulling the posts, as I had done the posts manually, I downloaded all of the assets and placed them in the assets folder of the Gatsby setup.\nIt was not easy doing it manually, I believe it would be interesting to have a library that would do all of these to make future migrations easier. I might someday give it a thought.\nHosting the website With Netlify, I faced no issue hosting the website and it was for free.\nCreating a Netlify account is a breeze considering the amount of options that they provide, my account with them uses the Github integration, and it was pretty fast.\nAfter my authentication and confirmation, I created a new application selecting Github as my code source. Netlify\u0026rsquo;s bot the rest of the process (building and serving the application).\nIn a matter of few minutes, the website came to live under a subdomain of Netlify which is the name of the application. The name looked really scary, so I changed that to reflect my name (aleemisiaka, of course).\nAnd there we have it.\nAt the time of this writing, I have not pointed my domain to the Netlify application. I hope to move my domain registrar to Netlify, at least a means of saying \u0026ldquo;Thank you!\u0026rdquo;\nConclusion It was not a rocket science. It was a matter of what needed to be done, and had to be done.\nI am so glad to be part of the Gatsby ecosystem, I would look into projects that I can contribute to. If you have one, 📭 shoot me a dm!\n🙇 Salut!\n","permalink":"https://limistah.dev/posts/migrating-from-wordpress-to-gatsby/","summary":"\u003cp\u003eYes, this topic is worth a post and I will walk you through why the migration was inevitable, and how I had done it.\u003c/p\u003e\n\u003cp\u003eFormerly, this website used to run on WordPress, the \u003cem\u003egreat\u003c/em\u003e PHP CMS. The design was made with the aid of Typography theme, I loved it!\u003c/p\u003e\n\u003cp\u003eAll of the post prior to this date was written using the WordPress admin dashboard. I loved the Gutenberg Editor, it was so fluid and easy to work with. Overall my experience was great.\u003c/p\u003e","title":"Migrating from WordPress to Gatsby - The Journey"},{"content":"Let\u0026rsquo;s be guided, we are not getting naughty here.\nNevertheless, a programmer is a human, he has his feelings and emotions which is interesting. The direction he puts these uniquely human traits makes him more interesting - towards his code and software, of course.\nI\u0026rsquo;ve been through a variety of programming languages, which always turned to be that the deeper I dive into a language, the more \u0026ldquo;aha\u0026rdquo; moment I get. It always feel like I have gotten what I wanted and I have to look no further in my quest to attract the priceless attention of my peers.\nIn my quest, I have seen unique features, improvements scattered in the different languages. Each time I had noticed a better way of doing things different from a previous language, I would give myself the accomplishment smile thinking I found what I wanted. And that my quest for a sexy language was over, the unlucky me; this has always been a repeating pattern.\nLooking at the below elixir snippet, where returned values from function calls can be pipped instead of being wrapped in parentheses or pass down as parameters. My reaction on seeing this was a big grin inside of my small intestine, a sweet sensation of coolness - what programmers love to have aside from the actual \u0026ldquo;sexiness\u0026rdquo; inborn with them.\nWhat I believe many other programmers like me failed to realize is a fact that these languages are tools, same with libraries and frameworks. Looking through the eye of a Mechanical Engineer, I understand that a Pipe Wrench even though looks like an Adjustable Spanner, can not function as one. Although they can be adapted in place of each other in some cases.\nPHP/Laravel, NodeJS/Express, Java/Spring, Python/Django, etc. should not be and can never be a debate of which is more beautiful and sexy, it is just baseless. Same way as asking Beyonce and Rihanna who\u0026rsquo;s more beautiful? Really?\nI understand that a programmer\u0026rsquo;s delight, sometimes, is to look more sophisticated than his peers in terms of tooling and machine. Does this really worth the fight? Do we have to keep asking who has more fan base, use case, GitHub stars, recently used and likes and not which actually gets these kinds of tasks done and which pays more?\nAll the tools that exist for us as a programmer have tradeoffs. Inventors of these tools tried to solve one major problem with them, and we could notice that these tools have thrived in their domains and would continue to thrive as long as there is no better replacement for those domains.\nC might be outdated, Fortran might be dead, Cobol could be seen resting in Valhalla, does that mean that people have forgotten about them? Does that mean the works done with them too are dead? Do we have to rewrite all the whole of an application just because of a new trendy tool?\nNo, we do not and we should not. To those that find those old fashioned languages interesting, let them have their time and space to prove their beauty. Even transitioning from an old language to a new one requires the knowledge of both languages, and does not translate to that old language being dead.\nUntil we start thinking of programming as a business and a means to an end, which actually takes out the sexy thoughts of a macho man, the quest of being that sexy programmer will always remain.\nI suggest, we all should start seeking for what actually brings in the bags and not the bugs. We can wander around these languages to expand our horizons and that should translate to the thoughts of being more fashionable. It should always be about the bank account, and not more macho.\n","permalink":"https://limistah.dev/posts/programmer-sexiness-quest/","summary":"\u003cp\u003eLet\u0026rsquo;s be guided, we are not getting naughty here.\u003c/p\u003e\n\u003cp\u003eNevertheless, a programmer is a human, he has his feelings and emotions which is interesting. The direction he puts these uniquely human traits makes him more interesting - towards his code and software, of course.\u003c/p\u003e\n\u003cp\u003eI\u0026rsquo;ve been through a variety of programming languages, which always turned to be that the deeper I dive into a language, the more \u0026ldquo;aha\u0026rdquo; moment I get. It always feel like I have gotten what I wanted and I have to look no further in my quest to attract the priceless attention of my peers.\u003c/p\u003e","title":"Programmer's Sexiness Quest"},{"content":"In this post, we will explore how to set up a NodeJS app on Amazon’s Lightsail instance. We will also explore setting up a CircleCI job for a NodeJS project, use Nginx as a web server, setup SSL for the server, and allow a local machine to access the remote server.\nPrerequisites AWS Lightsail instance AWS Route53 domain (not mandatory) CircleCI account Github repo for the NodeJS project. Let’s begin!\nSpinning up an instance I have spun up a $5 instance with my account, it comes with 1 GB RAM, 1 vCPU, 40 GB SSD. Below is the image configuration I used while setting up the instance:\nAWS Lightsail is configured to use a dynamic IP each time the server is restarted, a basic sudo reboot would give the server another IP address, we should configure a static IP for our server. This will save us the dynamic IP rotation.\nAdding static IP to an instance On the instance dashboard that you have created, click on the Network tab, then Attach static IP button.\nIt will toggle a select input to pick from existing static IPs that are not yet attached to an instance or create one. I have created one already so I only selected from the list, then the green button to confirm my selection. If you do not have one, enter a name for the IP then click on create button to create one, after which you can then attach to the instance.\nEither way, we now have a static IP attached to our Lightsail server. Cool isn’t it?\nAllowing SSH access from a local machine AWS Lightsail comes with a web-based terminal that runs like a regular UNIX terminal. I do not feel at home with it, so, I will be setting up an SSH access for the instance. This will improve my productivity working with the VPS.\nFrom the Lightsail home page, choose the instance you are working with, then click on the connect using SSH button.\nA new browser window should pop up, you should see:\nNext, from the command prompt run:\n1 sudo nano ~/.ssh/authorized_keys You should be greeted with a page containing default ssh key for your instance(more on this later). We will be adding our local machine’s public ssh key below the key already in the authorized_keys file.\nFrom your local machine, create an ssh key or use an existing one. I am using an already existing one, the command below will copy my id_rsa.pub file to my clipboard.\n1 xclip -sel clip \u0026lt; ~/.ssh/id_rsa.pub Back in the web terminal, from the interface, there is a clipboard icon, click on it a text area should appear at the top of the icon. Click into the text box, then press Ctrl+V or Cmd+V to paste the contents from your local clipboard into the browser-based SSH client clipboard. Right-click any area on the SSH terminal screen to paste the text from the browser-based SSH client clipboard to the terminal screen. Easy, right?\nTo exit the edit window, press Ctrl+X, press Y to confirm the modification, and ENTER to complete the process.\nFinally, reboot the server to reflect our changes:\n1 sudo reboot From our local machine’s terminal, we can run:\n1 ssh ubuntu@LIGHT_SAIL_STATIC_IP Replace LIGHT_SATIL_STATIC_IP with the static IP attached to your instance. If all goes well, you should be welcomed with the same message as the web terminal does. Well done!!!\nFinally, for our AWS setup, we can go further to mask our static IP to a domain name. AWS Route53 can do that for us easily, I won’t be covering that here, you can look up this article for the process.\nInstalling NodeJS To run our NodeJS code on the server, we need to install NodeJS runtime. I followed a tutorial from Tecadmin, here are the commands to do the installation:\n1 2 3 4 5 sudo apt update sudo apt install -y mongodb sudo systemctl enable mongodb sudo systemctl start mongodb sudo systemctl status mongodb The last command should give a working systemd status with a green highlight:\nAdding a new user account for deployment To deploy your app, you will require a copy of your NodeJS app code on the server. It is a good practice to have a user account whose sole purpose is to serve your app.\nTo create a new user for your Ubuntu server, running the below command will create a new user dkapi it will also create a home directory for the user at /home/dkapi\n1 sudo useradd -m -d /home/dkapi dkapi Next, you should add a password for the user that you just created, the command below will prompt for a password to be used for dkapi.\n1 sudo passwd dkapi You should be able to run commands as sudo user with the newly created user account. The below command will allow that to be possible:\n1 usermod -aG sudo dkapi Now, you can switch to the user you just created with:\n1 su - dkapi Provide the password you entered when creating the account, you should find a prompt with a $_ sign, that’s a success. Cool!\nCreating ssh keys for Github access I followed the guide by Github to create an SSH key from here. Then, add the generated ssh to your Github account using the guide from here. Finally, we can test our Github SSH set up with the guide from here.\nPulling the NodeJS codebase from Github We are here, you can now pull your NodeJS code from Github. This is basically cloning the repo, and this can be done easily with:\n1 git clone GITHUB_SSH_URL ~/app The repo will be cloned into ~/app directory\nFollow the guide here to install Yarn on the instance, after that you can install the dependencies using yarn command.\nRunning NodeJS app in the background For perfect integration with Nginx, you need to run your NodeJS app in the background. Packages like forever, pm2 gives this process a breeze. But, for me, I think using a systemd service is more appropriate, as I can start, stop and enable autostart of the service whenever the server is restarted. With the SSH access,systemd service has an edge over an npm package.\nA systemd service are instruction files created at: /lib/systemd/system/ and the files are to end with .service extension. Full details about systemd can be found here. Let’s create one for ourselves.\nFirstly, run sudo nano /lib/systemd/system/dkapi.service you can name dkapi.serviceanything, just ensure that it ends with a.service` as the file’s extension.\nThe content of the file should look like:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 [Unit] Description=APP API Service After=network.target StartLimitIntervalSec=0 [Service] Type=simple Restart=always RestartSec=1 User=dkapi ExecStart=/usr/bin/node /home/dkapi/app/start.sh WorkingDirectory=/home/dkapi/app StandardOutput=syslog StandardError=syslog SyslogIdentifier=dk-api Environment= NODE_ENV=production [Install] WantedBy=multi-user.target Attention should be paid to User=dkapi that should be the user account you created for the deployment, ExecStart=/home/dkapi/app/start.sh this is the start command for the service, basically calling an executable start.sh file. The file in my own case contains:\n1 yarn start Ensure that this file can be executed, use sudo chmod +x start.sh for this.\nThe WorkingDirectory=/home/dkapi/app should be the directory you pulled the files from Github into. And Environment= NODE_ENV=production to tell the environment variable you want the service to have.\nNext, enter Ctrl+X then ENTER to save the file. Now, you have a service systemd that can be used to manage your NodeJS server instance.\nTo enable the server to start when the server boots up we can do: To do systemctl enable dkapi Now, we can manually start our service with:\nsudo systemctl start dkapi\nGreatness!!!\nInstalling Nginx webserver NodeJS app only creates a local server, to accept request over the internet, we need a web server that will make that a breeze. We can do Apache, but I love Nginx for its simplicity, we will be installing and configuring one for this setup.\nFrom your SSH accessed server terminal run the below command to update the installed packages:\n1 sudo apt update \u0026amp;\u0026amp; sudo apt upgrade After that, you can install Nginx with:\n1 sudo apt install nginx A successful installation will give you:\nVisiting the static IP attached to this instance gives the default welcome page for a successful Nginx installation. Awesomeness!!!\nNginx configuration for NodeJs server routing To ensure that requests are passed to your NodeJS app, you need to have the default Nginx configuration block point to your the local URL of the app.\nWe will begin this by configuring our Nginx webserver.\nChanging directory to nginx installation directory with: cd /etc/nginx/ In the sites-available directory, copy the default config file to default.bak using sudo cp default default.bak, then use sudo nano ./sites-available/default to edit the content of the file.\nHere is a configuration that receives a normal request from the client then transfer it to the node server we have set up above.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 server { server_name SERVER_NAME_OR_STATIC_IP; location = /favicon.ico { log_not_found off; access_log off; } location = /robots.txt { allow all; log_not_found off; access_log off; } location / { proxy_pass http://localhost:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection \u0026#39;upgrade\u0026#39;; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_cache_bypass $http_upgrade; } access_log /var/log/nginx/dkapi-access.log; error_log /var/log/nginx/dkapi-error.log error; } Change the SERVER_NAME_OR_STATIC_IP to your server name that points to the static IP, or just use the Lightsail server static IP.\nIf all went well, visiting the server name or static IP should yield the default response for our app. Mine:\nAdding free SSL by letsencrypt Sometimes, accessing our NodeJS app using normal unsecured HTTP would throw a warning screen or will not resolve. This is a security feature implemented by web browsers and hosting providers. To this, I always install SSL for nginx servers. We will be doing a setup with let’s encrypt as generally done.\nRun the below command to install certbot package.\n1 2 3 4 apt-get install software-properties-common add-apt-repository ppa:certbot/certbot apt-get update apt-get install python-certbot-nginx To set up an SSL for your domain, you can do:\n1 certbot --nginx It will check the CN (common name) in the existing Nginx configuration file, if not found then it will prompt you to enter the domain name.\nCertbot automation is smart! It will take care of all the necessary configurations to make your Nginx ready to serve over https.\nNot so fast, but we now have SSL support for our nginx web server at the specified domain name.\nTo allow HTTPS requests to come through into the VPS instance, you have to modify the firewall from the Network tab on the instance dashboard. Click on Add another, select HTTPS, then click on the green tick.\nIt should look something like this:\nAnd that’s it! Full HTTPS support without an extra.\nTaking things further If you have a build process and a Continous Integration, you should be thinking of setting it up. This would protect against the round trip of pulling and restarting our app on the server.\nI assume that the project has been set up on CircleCI already using a Github account. Let’s move forward.\nf you notice from the bottom page of the Lightsail server instance dashboard Connect tab, it states, “You configured this instance to use id_rsa (key_region) key pair.” This is a secret SSH key and can be managed from https://lightsail.aws.amazon.com/ls/webapp/account/keys.\nThe keys from the page above are private keys whose public keys are included in instance setups. Lucky enough, it has been included in my own setup and it is what CircleCI is requesting to access my server remotely. It is not included in the authorised_keys of the user account that you have created, you are to add the public key in there.\nTo get the content of the file, firstly download the ssh key attached to the instance, next use cat command to show the content of the file. In my own case:\n1 cat LightsailDefaultKey-us-east-1.pem On the project page in CircleCI dashboard, click the settings icon close to the project, then SSH Permissions. Click on the blue Add SSH Key button enter the domain name for the key, then paste the copy private key into the Private key input, click the light blue Add SSH Key button to complete the process.\nNew user accounts do not come with the private ssh configured, we have to manually add this ourselves. To do this, we can generate a public key from the downloaded private key using:\n1 ssh-keygen -y -f ~/.ssh/lightsail.pem \u0026gt; ~/.ssh/lightsail.pem.pub Then you can copy the content of ~/.ssh/lightsail.pem.pub to the authorized_keys file of the account for your app. Simply:\n1 2 su — dkapi sudo nano ~/.ssh/authorized_keys Paste the copied public key into the file, then Ctrl + X to save.\nBelow is the CircleCI config for my own project, yours might be different. The important part here is the command section.\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 version: 2 jobs: staging: docker: - image: circleci/node:10 steps: - run: name: Deploy API command: ssh -o \u0026#34;StrictHostKeyChecking no\u0026#34; api@staging.datingkinky.com \u0026#34;cd ~/app; git pull; yarn install --production; sudo systemctl restart dkapi\u0026#34; workflows: version: 2 staging: jobs: - staging: filters: branches: only: develop The emphasis is on this:\n1 ssh -o \u0026#34;StrictHostKeyChecking no\u0026#34; dkapi@staging.datingkinky.com \u0026#34;cd ~/app; git pull; yarn install --production; sudo systemctl restart dkapi\u0026#34; To note from the above command:\ndkapi@staging.datingkinky.com: We want to connect to the instance but as the dkapi user we created. cd ~/app: Change directory to the app directory we pulled the code into. git pull: Pull the code from Github to keep the code updated yarn install — production: Install dependencies that might have just been included. sudo systemctl restart dkapi: Restarts the dkapi service running the NodeJS app in the background using systemd service A perfect process right?\nPush a new code, let CircleCI do your deployment and restarting the node app for you. Sooo cool!!!\nBeautiful? Not yet!\nGotcha I ran into a failing build, which is a result of the systemctl command. The error states\nn o t t y p r e s e n t a n d n o a s k p a s s p r o g r a m s p e c i f i e d Basically, we have to bypass systemctl asking for password whenever we run it from a trusted machine. I found the solution from Stackoverflow.\nNow, we have to ssh as a root user, then run:\n1 sudo visudo The command will pop a file for editing, it is a delicate file that can mar all the effort we have put into the setup. What we have to do in here is to go to the end of the file to add:\n1 dkapi ALL = NOPASSWD: /bin/systemctl Replace dkapi with the user you created in for deployment.\nUse Ctrl+X to exit the file. Then, we can rerun the failed CircleCI workflow or push a new code to test the setup.\nIt ran successfully in my own case, I believe yours too would have been successful.\nAwesome!!! Highlights Spinup a Lightsail instance Add a static IP to the instance Allow SSH access from a local machine Installing NodeJS Installing MongoDB Adding a new user account for deployment Creating ssh key for Github access Pulling the NodeJS repo from Github Running NodeJS app in the background Installing Nginx webserver Nginx configuration for NodeJS server routing Adding free SSL by letsencrypt Integrate CircleCI CircleCI integration gotcha Conclusion Following through this post, we have configured a non-existent AWS Lightsail server instance to run a NodeJS app. We have been able to set up the code to run a systemd service for the app to be able to run in the background.\nWe added SSL and then finalized the setup with a CircleCI integration and a fix to a known error that could cause the build process to fail.\nI will like to learn if there is any part that could be improved, know about a bug that stops your own setup and learn more about the AWS Lightsail Servers.\nThank you for reading. I appreciate your patience!!!\n","permalink":"https://limistah.dev/posts/nodejs-lightsail-setup/","summary":"\u003cp\u003eIn this post, we will explore how to set up a NodeJS app on Amazon’s Lightsail instance. We will also explore setting up a CircleCI job for a NodeJS project, use Nginx as a web server, setup SSL for the server, and allow a local machine to access the remote server.\u003c/p\u003e\n\u003ch2 id=\"prerequisites\"\u003ePrerequisites\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca \n  href=\"https://lightsail.aws.amazon.com/ls/webapp/home/instances\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eAWS Lightsail instance\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://console.aws.amazon.com/route53/home\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eAWS Route53 domain (not mandatory)\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca \n  href=\"https://circleci.com/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eCircleCI account\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eGithub repo for the NodeJS project.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eLet’s begin!\u003c/p\u003e","title":"Complete NodeJS App Setup  on an AWS Lightsail VPS"},{"content":"The very first task that we do while starting to program is naming from the creation of directory to files, to classes, to functions. Name is everywhere, we cannot escape it, and we do so much of naming, we should be fulfilled if we can do it better.\nBeginners do marvel at how the seniors snap out great names that fit the context to be applied. How the names are crafted seem like magic, they end up seeing themselves not so close when they see that wack name their brain could best provide.\nFortunately, Micheal Jordan was not born a perfect athlete, and he learned it all. We can learn how to craft proper names by following a set of guidelines laid out by experts in the field of computer programming.\nIn this series, Clean Code – Rules For Name Crafting, we will be learning how to write proper names in our codebases and the rules to guide our thoughts when we require a name. All lessons are taken from Clean Code by Robert C. Martin, and the lessons here are going to be illustrated using JavaScript from the Java Robert used in his book.\nRobert stated below rules, and we also see them as essential for naming considerations in our codebase:\nAlways use intention revealing names Avoid disinformation Make meaningful distinctions Use pronounceable names Use searchable names Avoid encodings Avoid mental mappings Do not be cute Pick one word per concept Do not Pun Use solution domain names Use problem domain names Add meaningful context Do not add context gratuitously It is an undeniable list with thoughtful and straightforward rules, and we will be going over all of them throughout the next few posts.\nBy the end of this series, we would be informed of why we make bad names, and how we can make good ones. Let us get started with the Rules for name crafting – Part One.\nWe will meet on the other side!\n","permalink":"https://limistah.dev/posts/clean-code-series/","summary":"\u003cp\u003eThe very first task that we do while starting to program is naming from the creation of directory to files, to classes, to functions. Name is everywhere, we cannot escape it, and we do so much of naming, we should be fulfilled if we can do it better.\u003c/p\u003e\n\u003cp\u003eBeginners do marvel at how the seniors snap out great names that fit the context to be applied. How the names are crafted seem like magic, they end up seeing themselves not so close when they see that wack name their brain could best provide.\u003c/p\u003e","title":"Clean Code – Rules For Name Crafting (Series)"},{"content":"Currently, I find my self reading Clean Code by Robert Cecil Martin, and just completed the first three chapters. I am taking my time to digest the genius work of Robert, and I see it as a need to share what could be understood in just the first 50 pages of the book.\nTo Robert, spending time to write the right code is an investment in the future maintenance time of that code. You can probably relate how a poorly written code has made you brainstorm for hours if not days before you could make that simple one-line change.\nWhile some people see it as moving really fast and not paying the necessary attention to their codes, he mentioned how writing good code is like writing a novel. The flows, structure, and content arrangement should be perfectly laid out to make the reader not need a second glance before comprehending the information the writer is passing.\nIn his book, Robert stated a conundrum: “All developers with more than a few years of experience know that previous messes slow them down. And yet, all developers feel the pressure to make messes in order to meet deadlines.” Which he further agree to that true professionals the second part of the conundrum is actually wrong, as a saying goes no amount of wrongs can make a right.\nIf you have been in the freelance space, you probably must have experienced the second part of the above conundrum. We tie productivity to shipping in the least possible time, and not considering that the time we did not pay to think through writing good codes would be demanded the next time we visit them again.\nYou know you are working on clean code when each routine you read turns out to be pretty much what you expected. You can call it beautiful code when the code also makes it look like the language was made for the problem: Ward Cunningham\nCodes should be pretty straight forward and do what they state, you nod at good code when you read them from the first glance. Part of our responsibility is to make the language look simple. With Ward\u0026rsquo;s statement, it is not the language that makes programs look simple. It is actually the programmer that make the language appear simple!\nTo Robert, the ratio of time spent to write new code to the time spent reading old codes is 10:1. Experienced programmers can tell how much they have gone back and forth writing, deleting and commenting out codes in an effort to write a new one.\nIt takes constant practice to write good codes, it does not have to happen from the very first time of writing the codes. It could come after the code has been completed in an optimization process, the goal is not to leave a messy campground.\nChapter Three of the book commence the lesson on writing good codes, beginning with tips for writing a good function. There is a wealth of knowledge buried in this chapter, I will be sharing my thoughts and understanding about this some other time.\nWe are authors, and one thing about authors is that they have readers. Indeed, authors are responsible for communicating well with their readers, the next time you write a line of code, remember you are an author, writing for readers who will judge your effort. - Robert Cecil Martin\nBe an artist, draw beautifully. Be a writer, write understandably. Be a poet, write eloquently. Be a programmer, write good code.\n","permalink":"https://limistah.dev/posts/clean-code-review/","summary":"\u003cp\u003eCurrently, I find my self reading \u003cstrong\u003e\u003ca \n  href=\"https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eClean Code\u003c/a\u003e\u003c/strong\u003e by \u003ca \n  href=\"https://twitter.com/unclebobmartin?ref_src=twsrc%5Egoogle%7Ctwcamp%5Eserp%7Ctwgr%5Eauthor\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eRobert Cecil Martin\u003c/a\u003e, and just completed the first three chapters. I am taking my time to digest the genius work of Robert, and I see it as a need to share what could be understood in just the first 50 pages of the book.\u003c/p\u003e\n\u003cp\u003eTo \u003cstrong\u003eRobert\u003c/strong\u003e, spending time to write the right code is an investment in the future maintenance time of that code. You can probably relate how a poorly written code has made you brainstorm for hours if not days before you could make that simple one-line change.\u003c/p\u003e","title":"Clean Code – Brief Review"},{"content":"MongoDB ships with an easy mongod CLI command to start its server. For Linux users, there is an added level of flexibility using the Systemd service to manage foreground and background processes. To start a MongoDB server on the boot of a Linux machine, it is as easy as registering a service with systemd using:\n1 $ systemctl enable mongod.service Switching from a Linux machine to Mac, and after the successful installation of MongoDB, surely, there is a need to start the command in the background while the development process continues.\nTo this, there are --fork, --quiet, and --syslog. Which are command line parameters for the mongod command.\nTo further ease the use of this our new discovery, we can create an alias in our .bashrc file by appending this command at the end of the file:\n1 $ echo \u0026#39;start-mongo = sudo mongod --fork --syslog --quiet\u0026#39; \u0026gt;\u0026gt; ~/.bashrc Now, at the start of our Mac machine, we can do start-mongo to launch our mongod service.\nEasy enough! 🍧\n","permalink":"https://limistah.dev/posts/mac-mongodb-in-background/","summary":"\u003cp\u003eMongoDB ships with an easy \u003ccode\u003emongod\u003c/code\u003e CLI command to start its server. For Linux users, there is an added level of flexibility using the Systemd service to manage foreground and background processes. To start a MongoDB server on the boot of a Linux machine, it is as easy as registering a service with \u003ccode\u003esystemd\u003c/code\u003e using:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-0-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-1\"\u003e1\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e$ systemctl \u003cspan class=\"nb\"\u003eenable\u003c/span\u003e mongod.service\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003eSwitching from a Linux machine to Mac, and after the successful installation of MongoDB, surely, there is a need to start the command in the background while the development process continues.\u003c/p\u003e","title":"Running mongod service in the background – MAC OS"},{"content":"Beginners do believe there is a perfect structure for setting up a project, experienced programmers know this is so far from the truth. A perfect structure is only perfect for a specific project, while project requirement varies across specifications, a perfect project setup varies across projects.\nLooking further down project specifications, there are repeating patterns and procedures that make all the projects look like they are all doing the same thing. Create Read Update Delete (CRUD) was invented for this purpose, many applications are doing at least one of CRUD action, a tour around would reveal the truth about this.\nSoftware development frameworks came as a result of abstracting the repeating patterns in applications. For PHP there is Laravel, Yii, CakePHP, Codeigniter, etc. NodeJS has a handful of its own from Sails, to Hapi to FeathersJS, and likes, even CSS is not excluded with Bootstrap and Foundation as examples of frameworks based on it.\nSome application requires subtleness and fluidity that frameworks do not provide or provide too much of. An experience with writing a single about me web page does not demand the pulling of almighty Laravel. Same way, we can\u0026rsquo;t generically think we could build a really large app with the setup provided by a framework.\nWhile Development Framework is good, taking away the responsibility of having a section of our code being tested across use cases. We should not forget that the Framework could bloat or run out of use case for some project specifications. Sometimes, Frameworks provide too many details than we need or lesser than it we require.\nAs the goal of third-party applications is to reach the use case of a wider range of users. There is a good number of people that fall out and some do fall too deep into the use cases they have to provide. A good software delivery skill is being consciously aware of when to use frameworks and when to bootstrap a custom codebase specifically designed for the situation at hand.\nSome frameworks fall too good that we tend to believe they can fit into all the use cases we can think of. This thinking obscures the fact that there is no size that fits all. If the specs are critically analyzed, we will find out that the application structure falls in one of:\nThis is too little to go into a framework This fits perfectly into this/that Development Framework No, there is no known framework setup that can support this. Before reaching a conclusion using the above points, there some preliminary questions that should be asked. Questions like:\nHow much control does the core code require? Will there be frequent updates than the framework release timeline intervals? Can errors/bugs be looked over, is workaround allowed to be used Does the performance of the framework satisfy the application requirements? How knowledgable is the team/programmer handling the development? Could reported errors be delayed till the framework actually provide a fix? Does the software license provided by the framework not conflicts with the requirement of the project? The questions above are just a generic overview, some projects will have their own specific requirements which will pose some more interesting questions to reach a conclusion about the use of third party code in the application development.\nFrameworks are so good generally, but they are not always the perfect solution for some application development process. Take extreme caution when making a great decision for the base of an application. Rewrites are never interesting, they look like walking past a previous stage in life, and humans do not like repeating a life\u0026rsquo;s stage.\nBe guided!\n","permalink":"https://limistah.dev/posts/frameworks-not-perfect-solution/","summary":"\u003cp\u003eBeginners do believe there is a perfect structure for setting up a project, experienced programmers know this is so far from the truth. A perfect structure is only perfect for a specific project, while project requirement varies across specifications, a perfect project setup varies across projects.\u003c/p\u003e\n\u003cp\u003eLooking further down project specifications, there are repeating patterns and procedures that make all the projects look like they are all doing the same thing. Create Read Update Delete (CRUD) was invented for this purpose, many applications are doing at least one of CRUD action, a tour around would reveal the truth about this.\u003c/p\u003e","title":"Frameworks - Not always the solution"},{"content":"This is the first of the series – Clean Code – Rule For Name Crafting Series. In this post, we will be dealing with the first three rules stated by Robert in his Clean Code book and they are: always use intention revealing names, avoid disinformation, and make meaningful distinctions.\n#1: Always Use Intention Revealing Names To some profession, giving names that do not reveal what the item/object is about might be a norm, we have seen astroids named Iris, and a human named Mars. Imagine that you are writing an Airport Flight Management System, what could client or customer mean generally in the app? These are names that could apply differently based on the context that they are being used.\nGeneralization should be avoided when naming variables, classes, methods/functions. Names should mean what they actually are wherever they appear. You could name the client/customer variable to be passenger while in a Flight class and customer while in the Payment class. These are valid names that reveal what they are intended for.\nAnother version of non-intention revealing names is the use of first letters of the name. Take const p = this.customer, this pollutes that section of the codebase with mappings of a single alphabet, remember when you have to look up to where a variable is declared, you have a bad variable name.\nLet us consider this little code:\n1 2 3 4 5 6 7 8 9 10 11 const getThem = () =\u0026gt; { const list1 = new Array(); for (let x in theList) { if (theList.hasOwnProperty(x)) { if (x[0] === 4) { list1.push(x); } } } return list1; } It is obvious that there is no indication of what this code is doing. We have to ask questions like:\nWhat is the Them in getThem? What is the significance of the first element in each item of the list How can the returned value for this be consumed? A perfectly named and intention revealing code should answer all of these for you with you not asking.\nCheck out this modified version of the code:\n1 2 3 4 5 6 7 8 9 10 11 12 const getActiveUserGroup = (userGroups) =\u0026gt; { const activeUserGroups = new Array(); for (let userGroupId in userGroups) { if(userGroups.hasOwnProperty(userGroupId)) { const userGroup = userGroup[userGroupId]; if (userGroup[STATUS_INDEX] === ACTIVE_SATATUS) { activeUserGroups.push(userGroup); } } } return activeUserGroups; } The modified version of the code is so explanatory, in that it tells what it does and reads like a story. Debugging this code does not require a preprocess function of the brain.\n#2: Avoid Disinformation Using the code in the above section and looking through theList and list1, what do they tell, actually? What information are they telling our readers by list1?\nAs programmers, we have to avoid giving information that actually does tell what we don\u0026rsquo;t mean. You should avoid using an Array where you actually meant an Object, and do not use an Object where it is a String. Let the name tell what they actually do.\nSpelling similar concepts similarly is information. Using inconsistent spellings is dis- information. You should not use names whose differences are not easily noticed, do not use names like XYZControllerForEfficientHandlingOfStrings and XYZControllerForEfficientStorageOfStrings, how hard it is to spot the difference, very hard you see? One example of uninformative names is using f for fund, as we do while iterating a list of funds. - Robert C. Martin\nWe can consider the below snippet:\n1 2 3 4 5 6 7 8 let u = user; let n = \u0026#34;name\u0026#34; let cn = \u0026#34;\u0026#34; if (n === u.name) { cn = n } else { n = u.name; } The above code can is best understood at the very point it was written. It is likely impossible for the reader to actually know what each variable means, without looking further beyond this code block. A better version would look like:\n1 2 3 4 5 6 7 let searchName = \u0026#34;name\u0026#34;; let foundName = \u0026#34;\u0026#34; if (searchName === user.name) { foundName = searchName; } else { foundName = user.name; } Good variable naming is a critical skill that could make a codebase look like a dump yard.\n#3: Make Meaningful Distinctions You probably have been in a situation where you do not want a variable\u0026rsquo;s value to be overwritten, still, you need the same name while in the same scope. How did you handle that?\nSome programmers will call the first occurrence: theUser and the second user, some would do user1 and user2. What I used to do is user and deeper in the code I would do _user, all these are a bad variable naming skill.\nUsing noise words, numbers and special characters does not help to convey information about a name. Whenever there is a need for a name, you should know that there has been an activity that is a result of a reaction triggered by that section of the code. No two names can look alike if you have followed the first rule intention revealing.\nNoise words are meaningless and create redundancy. A variable should not appear in a variable name, a class should not appear with a class name, usernameString is not better than using just username, good code structure should not allow types to juggle at will.\nIn the absence of specific conventions, the variable moneyAmount is indistinguishable from money, customerInfo is indistinguishable from customer, accountData is indistinguish-able from account, and theMessage is indistinguishable from message. Distinguish names in such a way that the reader knows what the differences offer. - Robert C. Martin\nConclusion We have just learned the first three rules that guide good variable naming. We have come to realize _user is a bad variable name as well as theUser. Initial alphabet of a name is not revealing the intention. Every time you are thinking of a name, you should consider the information you want your future self to know about that name.\nIn the next post, we will continue picking up more rules as defined by Robert. Write good names, and reveal your intentions, write beautiful codes!\n","permalink":"https://limistah.dev/posts/clean-code-name-crafting-one/","summary":"\u003cp\u003eThis is the first of the series – \u003ca \n  href=\"/blog/clean-code-series\"\n  \u003eClean Code – Rule For Name Crafting Series\u003c/a\u003e. In this post, we will be dealing with the first three rules stated by \u003ca \n  href=\"https://twitter.com/unclebobmartin?ref_src=twsrc%5Egoogle%7Ctwcamp%5Eserp%7Ctwgr%5Eauthor\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eRobert\u003c/a\u003e in his \u003cstrong\u003e\u003ca \n  href=\"https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eClean Code\u003c/a\u003e\u003c/strong\u003e book and they are: \u003cem\u003ealways use intention revealing names, avoid disinformation, and make meaningful distinctions.\u003c/em\u003e\u003c/p\u003e\n\u003ch3 id=\"1-always-use-intention-revealing-names\"\u003e#1: Always Use Intention Revealing Names\u003c/h3\u003e\n\u003cp\u003eTo some profession, giving names that do not reveal what the item/object is about might be a norm, we have seen astroids named \u003ca \n  href=\"https://en.wikipedia.org/wiki/7_Iris\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eIris\u003c/a\u003e, and a human named \u003cem\u003eMars\u003c/em\u003e. Imagine that you are writing an Airport Flight Management System, what could client or customer mean generally in the app? These are names that could apply differently based on the context that they are being used.\u003c/p\u003e","title":"Rules For Name Crafting – Part One"},{"content":"Passing down functions as event handlers down to children components is a norm in the react world. It eases the communication flow, as the saying goes props down, functions up.\nThings get a little bit tricky when using a React Classical component. Functions have to maintain their scope for proper interaction with their declared class properties. You might not be lucky sometimes, so there has been a couple of workaround for this.\nBind that function! Yes, this is what happens. You bind function passing the scope of the class they have been declared in.\nI am considering a simple ButtonClickCounter component:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import React, { Component } from \u0026#34;react\u0026#34;; class ButtonClickCounter extends Component { constructor(props) { super(props); } state = { clickedCount: 0 }; handleButtonClick() { const clickedCount = this.state.clickedCount; console.log(\u0026#34;This Button Has Been Clicked: %s times\u0026#34;, clickedCount); this.setState({ clickedCount: clickedCount++ }); } render() { return ( \u0026lt;div\u0026gt; \u0026lt;span\u0026gt;Button clicked \u0026lt;em\u0026gt;{this.state.clickedCount}\u0026lt;/em\u0026gt; times\u0026lt;/span\u0026gt; \u0026lt;button onClick={this.handleButtonClick}\u0026gt;Button\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; ); } } If you try to run this code, things will work fine till you try hitting the Button. You will get a Reference Error, what did we do wrong?\nIt happens that the function will be triggered in another scope where I do not know - that is for React to determine. But, I need to communicate with our local state, so, I should bind that function!\nBinding on the component Easily I could bind the ButtonClickCounter\u0026rsquo;s scope to the handleButtonClick handler by calling .bind while passing it.\nThe above code would provide a lasting solution to my scope problem, easy!\nBUT!!! What if I have some other button that uses the same function as an event handler? I would have to do .bind(this) on all the references?\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const render = () =\u0026gt; { return ( \u0026lt;div\u0026gt; \u0026lt;span\u0026gt; Button clicked \u0026lt;em\u0026gt;{this.state.clickedCount}\u0026lt;/em\u0026gt; times \u0026lt;/span\u0026gt; \u0026lt;button onClick={this.handleButtonClick.bind(this)} \u0026gt;Button\u0026lt;/button\u0026gt; \u0026lt;button onClick={this.handleButtonClick.bind(this)} \u0026gt;Button\u0026lt;/button\u0026gt; \u0026lt;button onClick={this.handleButtonClick.bind(this)} \u0026gt;Button\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; ); }; That is not supposed to be, let us step up a bit!\nBinding in the constructor Generally, what you will find around is binding a function in the constructor, this helps to reduce the redundancy allowed by binding on the component.\n1 2 3 4 constructor(props) { super(props); this.handleButtonClick.bind(this); } So, the component after proper binding would look like this:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import React, { Component } from \u0026#34;react\u0026#34;; class ButtonClickCounter extends Component { constructor(props) { super(props); this.handleButtonClick.bind(this); } state = { clickedCount: 0 }; handleButtonClick() { const clickedCount = this.state.clickedCount; console.log(\u0026#34;This Button Has Been Clicked: %s times\u0026#34;, clickedCount); this.setState({ clickedCount: clickedCount++ }); } render() { return ( \u0026lt;div\u0026gt; \u0026lt;span\u0026gt; Button clicked \u0026lt;em\u0026gt;{this.state.clickedCount}\u0026lt;/em\u0026gt; times \u0026lt;/span\u0026gt; \u0026lt;button onClick={this.handleButtonClick}\u0026gt;Button\u0026lt;/button\u0026gt; \u0026lt;/div\u0026gt; ); } } Can\u0026rsquo;t we get a little bit fancier without binding at all?\nOf Course\u0026hellip; Arrow function to the rescue I love arrow functions, since its introduction through ES6 and Babel, I have seen hundreds of it, and they look so beautiful!\nThe beauty that arrow functions have to offer is that they maintain the scope of where they are declared, unlike the traditional functions that maintain the scope of where they are called.\n1 const arrowFunction = () =\u0026gt; {} By simply converting handleButtonClick function to an arrow function instead, I am provided a neatly bounded function that could be used anywhere, and still can interact with my class properties as I might need.\n1 2 3 4 5 handleButtonClick = () =\u0026gt; { const clickedCount = this.state.clickedCount; console.log(\u0026#34;This Button Has Been Clicked: %s times\u0026#34;, clickedCount); this.setState({ clickedCount: clickedCount++ }); } Easy, right?\nConclusion You don\u0026rsquo;t always have to keep binding every function you pass down, by writing them as arrow functions, we get a little bit of extra power of being bound to the class\u0026rsquo;s scope.\nThis does not only assist us in keeping our code clean but it also saves us from bugs due to unbounded functions. And who does not like automation?\nSwitching to writing your arrow functions is a habit I would like you to form, I have formed it, and enjoying it. It is worth it!\n","permalink":"https://limistah.dev/posts/react-class-method-binding/","summary":"\u003cp\u003ePassing down functions as event handlers down to children components is a norm in the react world. It eases the communication flow, as the saying goes \u003cem\u003eprops down, functions up\u003c/em\u003e.\u003c/p\u003e\n\u003cp\u003eThings get a little bit tricky when using a React Classical component. Functions have to maintain their scope for proper interaction with their declared class properties. You might not be lucky sometimes, so there has been a couple of workaround for this.\u003c/p\u003e","title":"Smart React Class function scope binding"},{"content":"I\u0026rsquo;m Aleem Isiaka, a backend and cloud engineer, solutions architect with a deep commitment to building resilient, scalable systems that drive measurable impact. With nearly a decade of experience across backend development, infrastructure, and distributed systems, I specialize in architecting solutions that merge reliability, performance, and automation with end user experience.\nMy work sits at the intersection of site reliability engineering, cloud-native tooling, and scalable infrastructures, with hands-on contributions spanning Kubernetes, observability platforms, and infrastructure-as-code. From architecting high-throughput systems to reducing latency and improving uptime, I thrive in environments where scale and fault-tolerance are not just nice-to-haves but core requirements.\nI have led distributed teams, driven critical infrastructure overhauls, and delivered measurable impact, cutting response times by over 50%, increasing, and optimizing cloud operations across AWS, Azure, GCP, and Cloudflare ecosystems. I’m also an active contributor to the open-source community through Linux Foundation and personal initiatives like ObjectSpread and WitOps, helping engineers build smarter operational practices.\nBeyond code, I blog/vlog extensively about software engineering, and cloud migration strategies, sharing lessons learned to support the next generation of engineers. I also share my reading journey across research papers, journals, and books, rooted in a belief that engineering is as much about curiosity as it is about execution.\nI’m equally driven by a sense of purpose—whether it’s volunteering for open-source, exploring the beauty of system design, or asking the big questions about our universe. When I’m not building, I’m often journaling, learning history, or thinking deeply about how we can use engineering to solve problems in underserved communities.\nIf it must be done twice, I’ll automate it; if it must serve millions, I design it to scale.\nView my CV here\nAsante\n","permalink":"https://limistah.dev/about/","summary":"\u003cp\u003eI\u0026rsquo;m \u003cstrong\u003eAleem Isiaka\u003c/strong\u003e, a backend and cloud engineer, solutions architect with a deep commitment to building resilient, scalable systems that drive measurable impact. With nearly a decade of experience across backend development, infrastructure, and distributed systems, I specialize in architecting solutions that merge reliability, performance, and automation with end user experience.\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"Aleem Isiaka\" loading=\"lazy\" src=\"/assets/aleem-isiaka.png\"\u003e\u003c/p\u003e\n\u003cp\u003eMy work sits at the intersection of \u003cstrong\u003esite reliability engineering\u003c/strong\u003e, \u003cstrong\u003ecloud-native tooling\u003c/strong\u003e, and \u003cstrong\u003escalable infrastructures\u003c/strong\u003e, with hands-on contributions spanning \u003cstrong\u003eKubernetes\u003c/strong\u003e, \u003cstrong\u003eobservability platforms\u003c/strong\u003e, and \u003cstrong\u003einfrastructure-as-code\u003c/strong\u003e. From architecting \u003cem\u003ehigh-throughput systems\u003c/em\u003e to \u003cem\u003ereducing latency\u003c/em\u003e and \u003cem\u003eimproving uptime\u003c/em\u003e, I thrive in environments where scale and fault-tolerance are not just nice-to-haves but core requirements.\u003c/p\u003e","title":"About Me..."},{"content":"❓ Question Describe in your own words an example that requires sorting. Describe one that requires finding the shortest distance between two points.\n💡 Answer Example where sorting is required:\nOrganizing books in a library, could be sorted by date, author, publisher, ISBN etc.\nExample where finding the shortest distance between two points is required:\nA mailman trying to organize his daily deliveries would want the best route to save his time, also this helps to optimize the business operations.\n","permalink":"https://limistah.dev/introduction-to-algorithms/001-chapter-one/ex-1-1-1/","summary":"Solution to Exercise 1.1-1 Question","title":"Chapter One - Exercise 1.1-1"},{"content":"❓ Question Other than speed what measure of efficiency can you consider in a real-world setting?\n💡 Answer Latency can mean speed. Memory space is one important measure of efficiency considering how limited computer memories can get regardless of the advances made in the technology space. Others include Power Consumption, bandwidth usage, and scalability.\n","permalink":"https://limistah.dev/introduction-to-algorithms/001-chapter-one/ex-1-1-2/","summary":"Solution to Exercise 1.1-2 Question","title":"Chapter One - Exercise 1.1-2"},{"content":"❓ Question Select a data structure that you have seen and discuss its strengths and weaknesses.\n💡 Answer Data Structure: List\nStrength: It has a moderate lookup, insertion to the end is also fast same with deleting the last element.\nWeakness: It is very slow to delete an item in the middle or the first item, it is also very slow at inserting an item at the start. Deleting an item at the start and middle is slow as the list has to readjust its indexes while deleting the last item is fast considering the index stays the same only the length has to be readjusted.\n","permalink":"https://limistah.dev/introduction-to-algorithms/001-chapter-one/ex-1-1-3/","summary":"Solution to Exercise 1.1-3 Question","title":"Chapter One - Exercise 1.1-3"},{"content":"❓ Question How are the shortest path above and the traveling salesperson problems given above similar?\nHow are they different?\n💡 Answer Both problems are graph problems and seek the optimal path to fulfill certain criteria.\nThe difference is that while a Traveling Sales Person is seeking to tour all the nodes, the shortest path is seeking to tour a few nodes optimally.\nIn the shortest path, when represented as a graph the objective is to find the smallest weight along a path between the two nodes.\nIn traveling salesperson the object is to minimize the total distance traveled in a tour, visiting each node exactly once\n","permalink":"https://limistah.dev/introduction-to-algorithms/001-chapter-one/ex-1-1-4/","summary":"Solution to Exercise 1.1-4 Question","title":"Chapter One - Exercise 1.1-4"},{"content":"❓ Question Suggest a real-world problem in which only the best solution will do. Then come up with one in which “approximately” the best solution is good enough.\n💡 Answer Real-world problems where only the best solution will do:\nProblem: Flight Scheduling for Emergency Medical Transport\nDescription: In emergency medical situations, where time is critical, an organization is tasked with scheduling flights to transport medical supplies, organs for transplant, or medical teams to remote locations. The goal is to minimize the time from dispatch to arrival. Precision and minimizing delays are crucial in such cases, and only the best solution in terms of the shortest possible flight time and optimal scheduling will suffice.\nReal-world problems where approximately the best solution is good enough:\nProblem: Resource Allocation in Cloud Computing\nDescription: In a cloud computing environment, multiple users share resources such as virtual machines and storage. Allocating resources optimally to satisfy every user\u0026rsquo;s demand while minimizing costs is a complex problem. However, achieving an optimal solution might be impractical due to the dynamic nature of resource demands and the scale of the system. In this scenario, an approximately optimal solution that balances resource usage efficiently and meets the performance requirements of most users may be acceptable, as achieving absolute optimality might be computationally expensive and unnecessary in a rapidly changing environment.\n","permalink":"https://limistah.dev/introduction-to-algorithms/001-chapter-one/ex-1-1-5/","summary":"Solution to Exercise 1.1-5 Question","title":"Chapter One - Exercise 1.1-5"},{"content":"❓ Question Describe a real-world problem in which sometimes the entire input is available before you need to solve the problem. but other times the input is not entirely available in advance but arrives over time.\n💡 Answer Stock price analysis\nRealtime video content recommendation\nSocial media recommendation\nOnline retail inventory management\n","permalink":"https://limistah.dev/introduction-to-algorithms/001-chapter-one/ex-1-1-6/","summary":"Solution to Exercise 1.1-6 Question","title":"Chapter One - Exercise 1.1-6"},{"content":"❓ Question For each function $f(n)$ and time $t$ in the following table, determine the largest size $n$ of a problem that can be solved in time $t$, assuming that the algorithm to solve the problem takes $f(n)$ microseconds.\n💡 Answer We will provide a sample solution on how to solve for a $1sec$ for each of the running time.\n1 sec = 1000 millisecond = 1,000,000 microsecond = $10^6$ microsecond\n1 sec = $10^6$ microsecond\nSolving for $lgn$ where $lgn = 10^6$\n$2^{lgn} = 2^{10^6}$\n$n = 2^{10^6}$\nSolving for $\\sqrt{n}$ $\\sqrt{n} = 10^6$\n$(\\sqrt{n})^2 = (10^6)^2$\n$n = 10^{12}$\nSolving for $nlgn$\n$nlgn = 10^6$\n$f = nlgn - 10^6$\n$\\frac{df}{dn} = lgn + 1$\nWith an initial guess of $10^6$, we can use the Newton-Raphson method to get a solution\nSolution is: 62746.12646969076\nSee https://replit.com/@AleemIsiaka/nlgn-106#main.py\nSolving for $n^2 = 10^6$ $\\sqrt{n^2} = \\sqrt{10^6}$\n$n = \\sqrt{10^6} = 10^3$\nSolving for $n^3$ $n^3 = 10^6$\n$\\sqrt[3]{n^3} = \\sqrt[3]{10^6}$\n$n = 10^2$\nSolving for $2^n$ $2^n = 10^6$\n$lg 2^n = log 10^6$\n$n*lg2 = 6*lg10$\n$n = \\frac{6*lg10}{lg2} = \\frac{6*1}{0.30103} = 19.934$\n$n = 19.934$\nSolving for $n!$ $n! = 10^6$\n$n! = 1*2*3*4….n = 10^6$\nWe could pick a number as a guess, and check if the value is within $10^6$.\nif $n = 10$\n$10! = 3628800 (\u003e 10^6)$\n$9! = 637120 (\u003c 10^6)$\nHence $n = 9$, such that $n! ≤ 10^6$\nWe could do same for the rest of the time, changing the time value but running similar operations for the running times. 1sec 2min 1hr 1day 1mnt 1yr 1ctry $10^6$ $12*10^7$ $36*10^8$ $864*10^8$ $2592*10^9$ $31104*10^9$ $31104*10^{11}$ $lgn$ $2^{10^6}$ $\\sqrt{n}$ $10^{12}$ $nlgn$ $62746$ $n^2$ $10^3$ $n^3$ $10^2$ $2^n$ $19$ $n!$ $9$ ","permalink":"https://limistah.dev/introduction-to-algorithms/001-chapter-one/prbm-1-1-1/","summary":"Solution to Problem 1.1-1","title":"Chapter One - Problem 1.1-1"},{"content":"❓ Question Using Figure 2.2 as a model, illustrate the operation of INSERTION-SORT on an array initially containing the sequence \u0026lt;31, 41, 59, 26, 41, 58\u0026gt;\n💡 Answer p q r 31|41|59|26|41|58 Divide\np q r p q r 31|41|59 26|41|58 Divide\np,r p,q r p,r p,q r 31 41 | 59 26 41 | 58 Divide\np,r p,r p,r p,r p,r p,r 31 41 59 26 41 58 Merge\np,r p,q r p,r p,q r 31 41 | 59 26 41 | 58 Merge\np,q r p,q r 31|41 | 59 26|41 | 58 Merge\np q r 26|31|41|41|58|59 ","permalink":"https://limistah.dev/introduction-to-algorithms/002-chapter-two/ex-2-1-1/","summary":"Solution to Exercise 2.1-1 Question","title":"Chapter Two - Exercise 2.1-1"},{"content":"❓ Question Consider the procedure SUM-ARRAY on the facing page. It computes the sum of n numbers in array A[1:n]. State the loop invariant for this procedure, and use its initialization, maintenance, and termination properties to show that the SUM-ARRAY procedure returns the sum of the numbers in A[1:n].\n💡 Answer 1 2 3 4 5 SUM-ARRAY(A, n) sum = 0 for i = 1 to n sum = sum + A[i] return sum The procedure accepts and Array (A) and the length of the array(n)\nUsing loop invariant\nInitialization: By assigning i to 1 the loop is initialized, executing the vode inside of the body of the loop. The code takes the current value of i in this case 1 as an index in the Array A. It then pulls the value at that index and add it to the last value of the sum in this case zero, before reassigning that value to sum itself.\nMaintenance: The loop is maintained by incrementing the value of i, which in turn activates the body of the loop, pulling the value from the array at the index i, adding it with the previous sum, then storing it in the sum variable.\nTermination: The loop terminates when i is equal to n(the length of the array).\nAt the termination of the loop, all the items in the Array (A) must have been processed in the body of the loop.\nThe body of the loop stores the sum of A[1: i] by evaluating the sum of A[1 : i - 1] (i.e the sum of previous elements) and the value of A[i] .\nAt the end of the loop, sum = SUM(A[1:n])\nThe algorithm is correct.\n","permalink":"https://limistah.dev/introduction-to-algorithms/002-chapter-two/ex-2-1-2/","summary":"Solution to Exercise 2.1-2 Question","title":"Chapter Two - Exercise 2.1-2"},{"content":"❓ Question Rewrite the INSERTION-SORT procedure to sort into monotonically decreasing instead of monotonically increasing order.\n💡 Answer A monotonically decreasing order means starting from the highest element to the lowest: 23,22,21,20\nThe monotonically increasing order for insertion sort is:\n1 2 3 4 5 6 7 8 INSERTION-SORT (A, n) for i = 2 to n key = A[i] j = i - 1 while j \u0026gt; 0 AND A[j] \u0026gt; key A[j+1] = A[j] j = j - 1 A[j + 1] = key The monotonically decreasing order would be:\n1 2 3 4 5 6 7 8 INSERTION-SORT (A, n) for i = n - 1 to 1 key = A[i] j = i + 1 while j \u0026lt; n AND A[j] \u0026lt; key A[j-1] = A[j] j = j + 1 A[j - 1] = key ","permalink":"https://limistah.dev/introduction-to-algorithms/002-chapter-two/ex-2-1-3/","summary":"Solution to Exercise 2.1-3 Question","title":"Chapter Two - Exercise 2.1-3"},{"content":"❓ Question Consider the searching problem:\nInput: A sequence of n numbers $$ in array $A[1:n]$ and a value $x$\nOutput: An index $i$ such that $x$ equals $A[i]$ or the special value $NIL$ if $x$ does not appear in $A$.\nWrite a pseudocode for linear search, which scans through the array from beginning to end, looking for $x$ and using a loop invariant, proving that your algorithm is correct. make sure that your loop invariant fulfils the three necessary properties.\n💡 Answer Linear search on Wikipedia\nPseudocode:\n1 2 3 4 5 LINEAR-SEARCH A, n, x for i = 1 to n if A[i] = x return i return NIL Solution in Python\n1 2 3 4 5 def linear_search(arr, target): for i in range(len(arr)): if arr[i] == target: return i return None Example usage:\n1 2 3 4 5 6 7 8 test_list = [5, 8, 2, 9, 3, 6] target_element = 9 index = linear_search(test_list, target_element) if index == None: print(f\u0026#34;Element {target_element} not found.\u0026#34;) else: print(f\u0026#34;Element {target_element} found at index {index}.\u0026#34;) Loop Invariant:\nInitialization: The loop is initialized by setting $i$ to 1 – the first index of the array. If the array is empty, the loop is not initialized.\nMaintenance: The loop is maintained by increasing the value of $i$ till the end of the last element in the array $A$.\nTermination: The loop is terminated when a desired element is found or when the $i$ is greater than the length of the array.\n","permalink":"https://limistah.dev/introduction-to-algorithms/002-chapter-two/ex-2-1-4/","summary":"Solution to Exercise 2.1-4 Question","title":"Chapter Two - Exercise 2.1-4"},{"content":"❓ Question Consider the problem of adding two $n-bit$ binary integers $a$ and b, stored in two n-element arrays $A[0: n-1]$ and $B[0: n-1]$, where each element is either 0 or 1, $a = \\sum_{i=0}^{n-1} A[i]⋅2^i$ and $b = \\sum_{i=0}^{n-1} A[i]⋅2^i$. The sum $c = a + b$ of the two integers should be stored in binary form in an (n + 1)-element array C[0:n], where $c = \\sum_{i=0}^{n} C[i]⋅2^i$.\nWrite a procedure ADD-BINARY_INTEGERS that takes as input arrays $A$ and $B$ along with the length n, and returns array $C$ holding the sum.\n💡 Answer Assumption: The binary numbers are ordered while entering them into an array, e.g 12 which is $1100$ will look like so $[1,1,0, 0]$ with the last zero occupying the $n-1th$ index(LSB) and the first 1 occupying the $0th$ index(MSB).\nAn efficient algorithm could use bit masking(^, |, \u0026amp;) operators to do the addition operations.\nWe chose to be more explicit with the operations and leave the optimization for anyone who requires it.\nPseudo Code\n1 2 3 4 5 6 7 8 9 ADD-BINARY_INTEGERS (A, B, n) n_c = n + 1 // number of elements in array A + 1(to be used for the MSB) carry = 0 C = [] for i = n to 0 C[i] = (A[i] + B[i] + carry) mod 2 // get the remainder of the division in bit carry = ⌊(A[i] + B[i] + carry) / 2⌋ i = i - 1 C[n] = carry Implementation in Python\n1 2 3 4 5 6 7 8 9 def add_binary_integer(A: list, B: list): n = len(A) carry = 0 C = [0] * (n + 1) for i in range(n - 1, -1, -1): C[i + 1] = (A[i] + B[i] + carry) % 2 carry = (A[i] + B[i] + carry) // 2 C[0] = carry return C Implementation in C\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include \u0026lt;stdio.h\u0026gt; void add_binary_integer(int A[], int B[], int C[], int n) { int carry = 0; for (int i = n - 1; i \u0026gt;= 0; i--) { C[i + 1] = (A[i] + B[i] + carry) % 2; carry = (A[i] + B[i] + carry) / 2; } C[0] = carry; } int main() { int A[] = {1, 1, 0, 0}; // 12 in binary (natural order) int B[] = {1, 0, 1, 1}; // 11 in binary (natural order) int n = sizeof(A) / sizeof(A[0]); int C[n + 1]; add_binary_integer(A, B, C, n); printf(\u0026#34;Result: \u0026#34;); for (int i = 0; i \u0026lt;= n; i++) { printf(\u0026#34;%d \u0026#34;, C[i]); } printf(\u0026#34;\\n\u0026#34;); return 0; } ","permalink":"https://limistah.dev/introduction-to-algorithms/002-chapter-two/ex-2-1-5/","summary":"Solution to Exercise 2.1-5 Question","title":"Chapter Two - Exercise 2.1-5"},{"content":"❓ Question Express the function $n^3/100 + 100n^2 - 100n + 3$ in terms of $\\Theta$- notation.\n💡 Answer We can express it as $n^3 * 1/100 + 100 * n^2 + 100 * n + 3$\nMaking 1/100 as a, and the first 100 as b and the last 100 as c\n$a * n^3 + b * n^2 + c * n + 3$\n$an^3 + bn^2 + cn + 3$\nIgnoring the coefficients\n$n^3 + n^2 + n$\nThe running time is a quadratic function of n.\n$ \\Theta(n^3) + \\Theta(n^2) +\\Theta(n)$\nSuch that $n^3$ \u0026gt; $n^2$ \u0026gt; $n$.\nThe most significant part of the running time is $\\Theta(n^3)$ as we can eliminate the less significant part.\nSo,\n$n^3 * 1/100 + 100 * n^2 + 100 * n + 3$ = $\\Theta(n^3)$\n","permalink":"https://limistah.dev/introduction-to-algorithms/002-chapter-two/ex-2-2-1/","summary":"Solution to Exercise 2.2-1 Question","title":"Chapter Two - Exercise 2.2-1"},{"content":"❓ Question Consider sorting $n$ numbers stored in array $A[1:n]$ by first finding the smallest element of $A[1:n]$ and exchanging it with the element in $A[1]$. Then find the smallest element of $A[2:n]$, and exchange it with $A[2]$. Then find the smallest element of $A[3:n]$, and exchange it with $A[3]$. Continue in this manner for the first $n - 1$ elements of $A$.\nWrite the pseudocode for this algorithm, which is known as selection sort. What loop invariant does this algorithm maintain? Why does it need to run for only the first $n - 1$ elements, rather than for all $n$ elements? Give the worst-case running time of selection sort in $\\Theta$-notation. Is the best-case running time any better?\n💡 Answer Algorithm\n1 2 3 4 5 6 7 8 9 10 11 12 SELECTION-SORT (A, n) for i = 1 to n min_index = i for j = i + 1 to n: if A[j] \u0026lt; key min_index = j SWAP(A, i, j) SWAP(A, i, j) temp = A[i] A[i] = A[j] A[j] = temp Loop Invariant\nInitialization:\nMaintenance:\nTermination:\nIn the case of selection sort, all the elements at $A[1:i]$ are sorted containing an increasing order of the smallest values in the array while the sub array $A[i:n]$ remains unsorted. To sort the unsorted part, we need to find the smallest element from $A[i+1:n]$ and swap with $A[i]$, removing the need to start searching from the first element which in turn could mean looping through the inner loop for every element, we only loop through the entire loop in $n-$ times for the first element, and for each element $A[i]$, we have to loop $n - i$ elements to get the array sorted.\nThe worst case running time would occur at the first element. The Outer loop runs at $\\Theta(n)$ for the first item at index $i$, but to get the index of the lowest element from the sub array A[$i+1$: $n$], the inner loop has to run $n - i + 1$ times comparing each element to determine the lowest. Combining both run times $(n)(n-)$ becomes $n^2 - n$, and taking away the lower order terms and constants, the worst case running time is $\\Theta(n^2)$.\nThe best case running time would still be $\\Theta(n^2)$ considering the fact that the lowest element of the subarray must be determined. If $A[1: i]$ is sorted, and $A[i+1:n]$ is an unsorted subarray, and we have to sort the next smaller element by swapping $A[i+1]$ with any smaller element from $A[i+2:n]$. If the lowest element occurs at $j = i+2$ – the first element from the unsorted subarray, the inner loop can’t halt operation at this point, it has to continue to assert that no other element is lower than $A[j]$ from $A[j+1: n]$, even if from this point onwards all the elements in the subarray are larger than $A[j]$.\n","permalink":"https://limistah.dev/introduction-to-algorithms/002-chapter-two/ex-2-2-2/","summary":"Solution to Exercise 2.2-2 Question","title":"Chapter Two - Exercise 2.2-2"},{"content":"❓ Question Consider linear search again (see Exercise 2.1-4). How many elements of the input array need to be checked on average, assuming that the element being searched for is equally likely to be any element in the array? How about in the worst case? Using $\\Theta$-notation, give the average-case and worst-case running times of linear search. Justify your answers.\n💡 Answer Linear search is an $\\Theta(n)$ algorithm as all the elements in the array have to be visited in the case when the required element is the last element in the array. This can also be categorized as the worst case scenario.\nWorst case $O$: The element is at the end of the array, hence, $n$ elements have to be visited. $O(n)$\nAverage case $\\Theta$: We could assume the element to be found at the middle or the beginning of the array, this is wild. On average, the element can be found anywhere from the first to the $n_{th}$ element, ignoring the low order terms and removing the constants, we will end up with the element likely being at the nth element, hence on average, it is $\\Theta(n)$:\n$\\frac{1 + 2 + 3 … n}{n}$\nbut\n$1+2+3…n = \\frac{n(n+1)}{2}$\n$\\therefore$\n$1 + 2 + 3 … n/n = \\frac{n(n+1)}{2} / {n}$\n$\\therefore$\n$\\frac{n(n+1)}{2}/n = \\frac{n+1}{2}$\nIgnoring constants and lower terms, we are left with\n$n$\n","permalink":"https://limistah.dev/introduction-to-algorithms/002-chapter-two/ex-2-2-3/","summary":"Solution to Exercise 2.2-3 Question","title":"Chapter Two - Exercise 2.2-3"},{"content":"❓ Question How can you modify any sorting algorithm to have a good best-case running time?\n💡 Answer The best running time is $0(1)$, which for sorting is not feasible – the best sorting algorithm runtime is $\\Theta(n)$.\nTo achieve $\\Theta(n)$ running time – which is the best runtime for any sorting algorithm, we could take a couple of approaches:\nWe don’t sort all. This is considering that the algorithm is already sorted. We can determine this in $\\Theta(n)$ time using a for loop.\nWhile the sorting algorithm is running, we could determine if the rest of the items are sorted already, and return early instead of passing through them casually, taking up some runtime. Many “pass‑based” sorts (bubble, cocktail, gnome, even insertion sort) can keep a flag that tracks “did I make any swaps (or shifts) on this pass?” If on the first pass you do zero swaps, you stop immediately—again Θ(n) work total.\n","permalink":"https://limistah.dev/introduction-to-algorithms/002-chapter-two/ex-2-2-4/","summary":"Solution to Exercise 2.2-4 Question","title":"Chapter Two - Exercise 2.2-4"},{"content":"❓ Question Using Figure 2.4 as a model, illustrate the operation of merge sort on an array initially containing the sequence $\u003c3, 41, 52, 26, 38, 57, 9, 49\u003e$\n💡 Answer p q r 3|41|52|26|38|57|9|58 Divide\np q r p q r 3|41|52|26 38|57|9|58 Divide\np,q r p,q r p,q r p,q r 3 | 41 52 | 26 38 | 57 9 | 58 Divide\np,r p,r p,r p,r p,r p,r p,r p,r 3 41 52 26 38 57 9 58 Merge\np,q r p,q r p,q r p,q r 3 | 41 26 | 52 38 | 57 9 | 58 Merge\np q r p q r 3 | 26 | 41 | 52 9 | 38 | 57 | 58 Merge\np q r 3 | 9 | 26 | 38 | 41 | 52 | 57 | 58 ","permalink":"https://limistah.dev/introduction-to-algorithms/002-chapter-two/ex-2-3-1/","summary":"Solution to Exercise 2.3-1 Question","title":"Chapter Two - Exercise 2.3-1"},{"content":"❓ Question The test in line 1 of the $MERGE-SORT$ procedure reads “if $p ≥ r$” rather than “if $p ≠ r$.“ If MERGE_SROT is called with $p \u003e r$, then the subarray $A[p:r]$ is empty.\nArgue that as long as the initial call of $MERGE-SORT(A, 1, n)$ has $n ≥ 1$, the test “if $p ≠ r$” suffices to ensure that no recursive call has $p \u003e r$.\n💡 Answer 💡 We can prove this using induction:\nBase Case: The initial call is $MERGE-SORT(A, 1, n)$ where $n \u003e 1$, $p = 1$, $r = n$ $\\therefore$ $p ≤ r$\nRecursive step: Assuming a recursive call is made with p ≤ r. Then:\nCompute $q = \\lfloor(p+r)/2\\rfloor$ The two recursive calls:\n$MERGE-SORT(A, p, q)$ Since $q = \\lfloor(p+r)/2\\rfloor$, then $p ≤ q ≤ r$ So, $p ≤ q$ ( and not $p \u003e q$) $MERGE-SORT(A, q+1, r)$ $q + 1 ≤ r$ only if $q \u003c r$ Since $q = \\lfloor(p+r)/2\\rfloor$, then $q ≤ r - 1$ $\\equiv$ $q + 1 ≤ r$ Therefore in all of the recursive calls\n$q + 1 ≤ r$ $\\equiv$ $p ≤ r$\nSo, $p \u003e r$ can never happen if $MERGE-SORT$ is only called when $p ≠ r$\n","permalink":"https://limistah.dev/introduction-to-algorithms/002-chapter-two/ex-2-3-2/","summary":"Solution to Exercise 2.3-2 Question","title":"Chapter Two - Exercise 2.3-2"},{"content":"❓ Question The test in line 1 of the $MERGE-SORT$ procedure reads “if $p ≥ r$” rather than “if $p ≠ r$.“ If MERGE_SROT is called with $p \u003e r$, then the subarray $A[p:r]$ is empty.\nArgue that as long as the initial call of $MERGE-SORT(A, 1, n)$ has $n ≥ 1$, the test “if $p ≠ r$” suffices to ensure that no recursive call has $p \u003e r$.\n💡 Answer 💡 We can prove this using induction:\nBase Case: The initial call is $MERGE-SORT(A, 1, n)$ where $n \u003e 1$, $p = 1$, $r = n$ $\\therefore$ $p ≤ r$\nRecursive step: Assuming a recursive call is made with p ≤ r. Then:\nCompute $q = \\lfloor(p+r)/2\\rfloor$ The two recursive calls:\n$MERGE-SORT(A, p, q)$ Since $q = \\lfloor(p+r)/2\\rfloor$, then $p ≤ q ≤ r$ So, $p ≤ q$ ( and not $p \u003e q$) $MERGE-SORT(A, q+1, r)$ $q + 1 ≤ r$ only if $q \u003c r$ Since $q = \\lfloor(p+r)/2\\rfloor$, then $q ≤ r - 1$ $\\equiv$ $q + 1 ≤ r$ Therefore in all of the recursive calls\n$q + 1 ≤ r$ $\\equiv$ $p ≤ r$\nSo, $p \u003e r$ can never happen if $MERGE-SORT$ is only called when $p ≠ r$\n","permalink":"https://limistah.dev/introduction-to-algorithms/002-chapter-two/ex-2-3-3/","summary":"Solution to Exercise 2.3-3 Question","title":"Chapter Two - Exercise 2.3-3"},{"content":"❓ Question Use mathematical induction to show that when n ≥ 2 is an exact power of 2, the solution of the recurrence:\n$T(n)=\\begin{cases} 2 \u0026 if n = 2,\\\\ \\\\ 2T(n/2) + n \u0026 if n \u003e 2. \\end{cases}$\nis\n$T(n) = n lg n$\n💡 Answer ","permalink":"https://limistah.dev/introduction-to-algorithms/002-chapter-two/ex-2-3-4/","summary":"Solution to Exercise 2.3-4 Question","title":"Chapter Two - Exercise 2.3-4"},{"content":"❓ Question You can also think of insertion sort as a recursive algorithm. In order to sort $A[1:n]$, recursively sort the subarray $A[1:n-1]$ and then insert $A[n]$ into the sorted subarray $A[1:n-1]$. Write the pseudocode for this recursive version of insertion sort. Give a recurrence for its worst-case running time.\n💡 Answer ","permalink":"https://limistah.dev/introduction-to-algorithms/002-chapter-two/ex-2-3-5/","summary":"Solution to Exercise 2.3-5 Question","title":"Chapter Two - Exercise 2.3-5"},{"content":"❓ Question Referring back to the searching problem (see Exercise 2.1-4), observe that if the subarray that is being searched is already sorted, the searching algorithm can check the midpoint of the subarray against $x$ and eliminate half of the subarray from further consideration. The binary search algorithm repeats this procedure having the size of the remaining portion of the subarray each time. Write pseudocode, either iterative or recursive for binary search. Argue that the worst-case running time of binary search is $\\Theta (lg n)$\n💡 Answer ","permalink":"https://limistah.dev/introduction-to-algorithms/002-chapter-two/ex-2-3-6/","summary":"Solution to Exercise 2.3-6 Question","title":"Chapter Two - Exercise 2.3-6"},{"content":"❓ Question The while loop of lines 5-7 of the INSERTION-SORT procedure in Section 2.1 uses a linear search to scan (backward) through the sorted subarray $A[1:i-1]$. What if insertion sort used a binary search (see Exercise 2.3-6) instead of a linear search? Would that improve the overall worst-case running time of insertion sort to $\\Theta(nlgn)$?\n💡 Answer ","permalink":"https://limistah.dev/introduction-to-algorithms/002-chapter-two/ex-2-3-7/","summary":"Solution to Exercise 2.3-7 Question","title":"Chapter Two - Exercise 2.3-7"},{"content":"❓ Question Although merge sort runs in $\\Theta(nlgn)$ worst-case time and insertion sort runs in $\\Theta(n^2)$ worst-case time, the constant factors in insertion sort can make it faster in practice for small problem sizes on many machines. Thus it makes sense to coarsen the leaves of the recursion by using insertion sort within merge sort when subproblems become sufficiently small. Consider a modification to merge sort in which $n/k$ sublists of length $k$ are sorted using insertion sort and then merged using the standard merging mechanism, where $ k$ is a value to be determined.\na. Show that insertion sort can sort the $n/k$ sublists, each of length $k$, in $\\Theta(nk)$ worst-case time.\nb. Show how to merge the sublists in $\\Theta(nlg(n/k))$ worst-case time.\nc. Given that the modified algorithm runs in $\\Theta(nlg(n/k))$ worst-case time, what is the largest value of $k$ as a function of $n$ for which the modified algorithm has the same running time as standard merge sort, in terms of $\\Theta$-notation?\nd. How should you choose k in practice?\n💡 Answer ","permalink":"https://limistah.dev/introduction-to-algorithms/002-chapter-two/prbm-2-1/","summary":"Solution to Problem 2.1","title":"Chapter Two - Problem 2.1"},{"content":"❓ Question Correctness of bubble sort\nBubble sort is a popular but inefficient sorting algorithm. It works by repeatedly swapping adjacent elements that are out of order. The procedure BUBBLESORT sorts array A[1:n]\n1 2 3 4 5 BUBBLESORT(A, n) 1 for i = 1 to n - 1 2 for j = n downto i + 1 3 if A[j] \u0026lt; A[j-1] 4 exchange A[j] with A[j-1] Let $A'$ denotes the array A after BUBBLESORT(A, n) is executed. To prove that BUBBLESORT is correct, you need to prove that it terminates and that $A’[1] ≤ A’[2] ≤ …. ≤ A’[n]$……………… (2.5) In order to show that BUBBLESORT actually sorts, what else do you need to prove? State precisely a loop invariant for the for loop in lines 2-4, and prove that this loop invariant holds. Your proof should use the structure of the loop-invariant proof presented in this chapter. Using the termination condition of the loop invariant proved in part (b), state a loop invariant for the for loop in lines 1-4 that allows you to prove inequality (2.5). Your proof should use the structure of the loop-invariant proof presented in this chapter. What is the worst-case running time of BUBBLESORT? How does it compare with the running time of INSERTION-SORT? 💡 Answer ","permalink":"https://limistah.dev/introduction-to-algorithms/002-chapter-two/prbm-2-2/","summary":"Solution to Problem 2.2","title":"Chapter Two - Problem 2.2"},{"content":"❓ Question Correctness of Horner’s rule You are given the coefficients $a_0, a_1, a_2, …. a_n$ of a polynomial\n$P(x) = \\sum_{k=0}^{n} a_kx^k$\n$P(x)= a_0, a_1x, a_2,x^2 …. a_{n-1}x^{n-1}+a_nx^n$\nand you want to evaluate this polynomial for a given value of x. Horner’s rule says to evaluate the polynomial according to this parenthesization:\n$P(x) = a_0 + x(a_1+x(a2+…+x(a_{n-1} + xa_n)…)).$\nThe procedure HORNER implements Horner’s rule to evaluate P(x), given the coefficients $a_0, a_2, …. a_n$ in an array $A[0:n]$ and the value $x$.\nIn terms of \\Theta-notation, what is the running time of this procedure? Write pseudocode to implement the naive polynomial-evaluation algorithm that computes each term of the polynomial from scratch. What is the running time of this algorithm? How does it compare with HORNER? Consider the following loop invariant for the procedure HORNER: At the start of each iteration of the for loop of lines 2-3, $p = \\sum_{k=0}^{n-(i+1)} A[k +i + 1] ⋅ x^k .$ Interpret a summation with no terms as equaling 0. Following the structure of loop invariant proof presented in this chapter, use this loop invariant to show that, at termination, $p = \\sum_{k=0}^{n} A[k] ⋅ x^k .$\n💡 Answer ","permalink":"https://limistah.dev/introduction-to-algorithms/002-chapter-two/prbm-2-3/","summary":"Solution to Problem 2.3","title":"Chapter Two - Problem 2.3"},{"content":"❓ Question Inversions Let $A[1:n]$ be an array of n distinct numbers. If $i \u003c j$ and $A[i] \u003e A[j]$, then the pair $(i, j)$ is called an inversion of $A$.\nList the five inversions of the array $\u003c2, 3, 8, 6, 1\u003e$ What array with elements from the set {1, 2, …. n } has the most inversions? How many does it have? What is the relationship between the running time of insertion sort and the number of inversions in the input array? Justify your answer. Give an algorithm that determines the number of inversions in any permutation on $n$ elements in $\\Theta(nlgn)$ worst-case time. (Hint: Modify merge sort.) 💡 Answer ","permalink":"https://limistah.dev/introduction-to-algorithms/002-chapter-two/prbm-2-4/","summary":"Solution to Problem 2.4","title":"Chapter Two - Problem 2.4"},{"content":"General Information This is commonly referred to as the Raft Paper.\nAuthor: Diego Ongaro, John Ousterhout\nPaper: https://raft.github.io/raft.pdf\nImplementation: https://github.com/objectspread/go-raft\n","permalink":"https://limistah.dev/journals/raft/","summary":"\u003ch3 id=\"general-information\"\u003eGeneral Information\u003c/h3\u003e\n\u003cp\u003eThis is commonly referred to as the Raft Paper.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eAuthor\u003c/strong\u003e: Diego Ongaro, John Ousterhout\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003ePaper\u003c/strong\u003e: \u003ca \n  href=\"https://raft.github.io/raft.pdf\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003ehttps://raft.github.io/raft.pdf\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eImplementation\u003c/strong\u003e: \u003ca \n  href=\"https://github.com/objectspread/go-raft\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003ehttps://github.com/objectspread/go-raft\u003c/a\u003e\u003c/p\u003e","title":"In Search of an Understandable Consensus Algorithm"},{"content":"Details Authors: Thomas H. Cormen, Charles E. Leiserson, Ronald Rivest, Clifford Stein\nPublic Exercise Solutions https://sites.math.rutgers.edu/~ajl213/CLRS/CLRS.html https://clrs.skanev.com My Chapter Notes Chapter One Chapter Two Chapter Three ","permalink":"https://limistah.dev/books/introduction-to-algorithms/","summary":"This book provides a comprehensive introduction to the modern study of computer algorithms. It presents many algorithms and covers them in considerable depth, yet makes their design accessible to readers at all levels.","title":"Introduction To Algorithms"},{"content":"Current Workspace Monitor Samsung 32\u0026quot; S3\nMobile iPhone 14 Pro\nKeyboard Keychron K9\nMicrophone Shure M7\nMouse Logitech MX Master\nWebcam Logitech C922\nLaptop Apple M4 Pro\nHomelab Dell 16GBRAM Corei7 ES470\nPrinter HP\nTech Stack Languages Rust, Golang, Python, TypeScript, C/C++\nOperating System MacOS, Linux\nPreferred Cloud GCP, AWS, Cloudflare\nEmployment Status I work as a backend engineer at Kredete, where I contribute to the development of payment infrastructures that power millions of users.\nRelationship Married to an amazing woman\nRecent Location Lagos, Nigeria\nEmigration now crosses my mind, with Germany and France topping the list; the skilled worker path should be a good fit here.\nEducation BSc. Computer Science - Kwara State University\nBEng. Mechanical Engineering - Kwara State University\nHND Mechanical Engineering - Lagos State Polytechnic\nND Mechanical Engineering - Kwara State Polytechnic\nI did not get to do my CKA/CKS exams in the last quarter of 2025, pushing that forward to another time in the future, but, digging deeper into AWS and GCP still in 2026!\nLearning Programming Languages C/C++, Rust\nCS Concepts Algorithms, Distributed Systems, DeFi, and Internet Computing\nCurrently Reading To Sell Is Human\nHow to Win Friends and Influence People\nSite Reliability Engineering\nModern Operating System\nThe C Programming Language\nIntroduction to Algorithms\nInterests Blogging and Journaling DeFi \u0026amp; Blockchain GNU/Linux and OSS DevOps, Automation \u0026amp; IAC Attending and Organizing Events Projects Got the design for WitOps, I don\u0026rsquo;t feel great about it, so we are going through a second phase of the redesign.\nDesign for ObjectSpread is underway, and the blog is now live on https://blog.objectspread.com. You could Join Us!!\nSince my website is more usable, I think it can serve me for the next 5 years (yaaay\u0026hellip;). The next focus is on redesigning ObjectSpread and WitOps.\nI still think hard on the project-per-quarter thingy, it still interests me to do! I started this already, and so far we have two projects:\nHeimdal: This is my solution to an effortless dev environment switch. Mustache-zig: A Mustache Spec implementations in Zig Markview: Paste or upload markdown, preview it, and share a link I have a private foundation that I passively operated, just in my mind, I was thinking of formalizing it, we shall see if this is still possible.\nVolunteering I help organize GopherCon Africa - The largest Go event in Africa.\nPart of the CNCF community - I focus on Kubernetes, Prometheus, and Jaeger.\n","permalink":"https://limistah.dev/now/","summary":"\u003ch3 id=\"current-workspace\"\u003eCurrent Workspace\u003c/h3\u003e\n\u003cp\u003e\u003cstrong\u003eMonitor\u003c/strong\u003e \u003ca \n  href=\"https://www.samsung.com/us/monitors/curved/32-inch-s3-s39gd-fhd-100hz-curved-monitor-with-speakers-sku-ls32d396ganxza/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eSamsung 32\u0026quot; S3\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eMobile\u003c/strong\u003e \u003ca \n  href=\"https://www.gsmarena.com/apple_iphone_14_pro-11860.php\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eiPhone 14 Pro\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eKeyboard\u003c/strong\u003e \u003ca \n  href=\"https://www.keychron.com/products/keychron-k6-wireless-mechanical-keyboard?srsltid=AfmBOor2iJ6KGCbT8oenZvnVCK5bVKYyTaBOAI2QyyGMBmwcVHfUJpRP\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eKeychron K9\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eMicrophone\u003c/strong\u003e \u003ca \n  href=\"https://www.shure.com/en-US/products/microphones/mv7\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eShure M7\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eMouse\u003c/strong\u003e \u003ca \n  href=\"https://www.logitech.com/en-ca/mx/master-series.html\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eLogitech MX Master\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eWebcam\u003c/strong\u003e \u003ca \n  href=\"https://www.logitech.com/en-us/shop/p/c922-pro-stream-webcam\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eLogitech C922\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eLaptop\u003c/strong\u003e \u003ca \n  href=\"https://www.apple.com/shop/buy-mac/macbook-pro/14-inch-m4-pro\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eApple M4 Pro\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eHomelab\u003c/strong\u003e Dell 16GBRAM Corei7 ES470\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003ePrinter\u003c/strong\u003e HP\u003c/p\u003e\n\u003ch3 id=\"tech-stack\"\u003eTech Stack\u003c/h3\u003e\n\u003cp\u003e\u003cstrong\u003eLanguages\u003c/strong\u003e Rust, Golang, Python, TypeScript, C/C++\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eOperating System\u003c/strong\u003e MacOS, Linux\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003ePreferred Cloud\u003c/strong\u003e GCP, AWS, Cloudflare\u003c/p\u003e\n\u003ch3 id=\"employment-status\"\u003eEmployment Status\u003c/h3\u003e\n\u003cp\u003eI work as a backend engineer at \u003ca \n  href=\"https://www.linkedin.com/company/kredete/\"\n   \n    target=\"_blank\" \n    rel=\"noopener\"\n  \u003eKredete\u003c/a\u003e, where I contribute to the development of payment infrastructures that power millions of users.\u003c/p\u003e","title":"Now..."},{"content":"C is a general-purpose programming language and is closely associated with Unix systems where it was developed. It is a machine independent language and often time called system programming language due to its usefulness in writing compilers and operating systems.\nC picked up ideas from BCPL developed by Martin Richards who took ideas from B developed by Ken Thompson in 1970 for the first UNIX systems using DEC PDP-7.\nBoth B and BCPL are untyped languages, whereas C has fundamental types like characters, integers, floating-point numbers, etc., and derived data types like pointers, arrays, structures, pointers, and unions. C has expressions and operands, where any expression can be a statement.\nC supports fundamental control flow constructions required for well-structured programs: statement groupings, decision-making selecting one of a set of possible cases, looping with termination test at the top or the bottom, and early loop exit.\nFunctions in C can return values of fundamental and derived data types. Local variables are automatic - does not need a manual creation process, and can be reinitialised with a new invocation. A function can not be nested: no function expression, but variables can be block-scoped. Function compiled differently from a source file can be used in another source file. Variables can be function scoped, or global scoped within a single source file or visible to the entire program. Macro substitution can be performed on program text, to include other source files, and conditional compilation using preprocessing steps.\nBecause C deals with the same set of objects that most computers do, the language can be said to be a relatively low-level language.\nThe language does not deal directly with composite objects and no operation that manipulates an array or string even though the structure may be copied as a unit. there is no storage allocation facility aside from static allocation and stack provided by variables and functions; there is no heap or garbage collection. Finally, C does not provide any I/O facility, most C implementations have included a reasonable standard collection of I/O functions.\nAlso, C offers only straightforward single-thread control flow: tests, grouping, and subprograms but no multiprogramming, parallel operations, synchronization, or coroutines.\nThe unavailability of these features seems like a great deficiency in the language, but this under-weighs the benefit of keeping the language at a modest size - it can be used in a very small environment and even learned quickly. A programmer can reasonably expect to know and understand and indeed regularly use the entire language.\nIn 1983, a modern, comprehensive definition of C was developed by a committee established by the American National Standards Institute (ANSI), which became the ANSI C and was completed in 1988 most of the features of the standards are already implemented by modern C compilers.\nC, like any other language has its blemishes. Some of the operators have the wrong precedence; some parts of the syntax could be better. Nonetheless, C has proven to be an extremely effective and expressive language for a wide variety of programming applications. - pg 3\n","permalink":"https://limistah.dev/books/c-programming-language/","summary":"\u003cp\u003eC is a general-purpose programming language and is closely associated with Unix systems where it was developed. It is a machine independent language and often time called system programming language due to its usefulness in writing compilers and operating systems.\u003c/p\u003e\n\u003cp\u003eC picked up ideas from BCPL developed by Martin Richards who took ideas from B developed by Ken Thompson in 1970 for the first UNIX systems using DEC PDP-7.\u003c/p\u003e\n\u003cp\u003eBoth B and BCPL are untyped languages, whereas C has fundamental types like characters, integers, floating-point numbers, etc., and derived data types like pointers, arrays, structures, pointers, and unions. C has expressions and operands, where any expression can be a statement.\u003c/p\u003e","title":"The C Programming Language"}]