<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Hussam Almarzooq | حسام المرزوق]]></title><description><![CDATA[I design and build systems of software and people | A Pythonista stuck in PhpStorm | VP of Engineering at Zid.sa]]></description><link>https://hussam.engineering/</link><image><url>https://hussam.engineering/favicon.png</url><title>Hussam Almarzooq | حسام المرزوق</title><link>https://hussam.engineering/</link></image><generator>Ghost 4.48</generator><lastBuildDate>Sat, 11 Apr 2026 13:38:52 GMT</lastBuildDate><atom:link href="https://hussam.engineering/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[The Making of the Top 1% Engineers]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>I get asked very frequently about my policy at Zid that we host exactly one intern/trainee at a time. The whole 6-month &#x201C;batch&#x201D; is one person. People at Zid and externally would say things like: why not have more? There are more people in the team who</p>]]></description><link>https://hussam.engineering/the-making-of-the-top-1-engineers/</link><guid isPermaLink="false">698f6ba016231504c3c42fda</guid><dc:creator><![CDATA[Hussam Almarzooq]]></dc:creator><pubDate>Sat, 11 Apr 2026 13:12:08 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>I get asked very frequently about my policy at Zid that we host exactly one intern/trainee at a time. The whole 6-month &#x201C;batch&#x201D; is one person. People at Zid and externally would say things like: why not have more? There are more people in the team who can coach. We need more capacity to work. Which are all valid thoughts, but they miss the context of what I am trying to achieve with these individuals.</p>
<p>I will discuss my perspective on the making of excellent engineers, which is a personal goal of mine that I have the virtue of being able to pursue as part of my job at Zid. One note before I get into it, the title might not be so accurate. I believe what I do would help push the person into the top 5% of engineers, but it is gonna be up to them to push themselves into the top 1%.</p>
<p>It mainly comes down to the fact that we are not a boot camp. Bootcamps admit people in relatively large batches, hoping that the majority will graduate with enough knowledge of the curriculum to secure a job. Output is consistent and predictable for both students and hiring companies. Every student more or less receives the same amount of attention and help, and they all go through the same material regardless of their background or competence.</p>
<p>We want to focus and personalize the experience for each individual&#x2019;s needs and skills. Having a big class of interns means that a mentor&#x2019;s attention would be divided among them. Each individual has different qualities and needs time in different areas. The mentor would either give generic feedback to everyone or put most time on a single individual, neglecting the rest.</p>
<p>One might ask what kind of attention I am referring to. The internship is not about getting a few small tasks assigned, then senior engineers give a few code review comments. That is just day-to-day work for anyone. I assign interns reading assignments in related areas where they need to read the chapter or paper, summarize it, and then object to it. This might sound like a school assignment, which I have to admit, it is. However, I choose topics that have direct applications and examples from our systems.</p>
<p>The point of this is that writing disciplines one&#x2019;s thinking. When they have to articulate all the mess of ideas in their head, put them in writing, they have to be more thorough. The point of summarizing is to ensure that they charitably<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> understand what the author is trying to convey, and do not jump to conclusions based on pre-conceived notions<sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>. Then the objection is to practice writing a well-articulated rationale, refining it, and defending it.</p>
<p>Reading, correcting, discussing, and objecting would all require time that cannot be divided reasonably across multiple people. Otherwise, feedback would be barely useful or would have minimal insight. Again, we are not a boot camp. We are not trying to be. We choose an individual and double down on our investment in them. Bootcamps try to graduate as many as possible who meet certain criteria.</p>
<p>So, what kind of individual am I looking for? I sum it up in two characteristics that influence everything else &#x2013;Sharp and thorough. Sharp in the sense that they move fast. They get things without repetition and with minimal input. And thorough in that they go into details, they do not just take things at face value or only learn the big picture. Sharp and thorough people tend toalso be assholes. Some level of asshole-ness is acceptable, but there has to be a threshold<sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>. We still want people to be confident and take pride in their work. I discussed how I test this in my post <em><a href="https://hussam.engineering/thoughts-on-technical-interviews">Thoughts on Technical Interviews</a>.</em></p>
<p>The other thing we look for, as defined in our role competencies, is being able to &#x201C;demonstrate above peer average knowledge.&#x201D; Meaning that we want Someone who already knows more than the basics in their field. Someone who, for example, knows how to build a web app, design a database, and architect a system. We do not want to start from nothing; we want someone who is okay (based on our measure), and we make them excellent.</p>
<p>I think one critical point that allows us to hold the bar so high is the strict rule against planning based on intern headcount. Interns are an &#x201C;extra&#x201D; capacity, a long-term investment. Leads and PMs are explicitly told to plan based on the full-time team members. And more importantly, we do not brag about the number of batches and quotas that we filled with interns. We do not think that this is something we should be chasing. These two reasons, combined, allow us to take our time interviewing tens of candidates and say no without fearing that we would miss a target.</p>
<p>By product of this competitiveness is exclusivity. I do make sure that every candidate understands the acceptance rate of the role, and the expectation out of them if they get the offer. I noticed that this exclusivity ensures that interns take their role seriously and want to succeed at it.</p>
<p>To sum it up, we do not need to hire interns; we choose to invest in them. We do not have a set program; we create a learning path for every individual to fill their gaps and capitalize on their interests. And lastly, look for the people who are sharp, thorough, and have above-peer knowledge.</p>
<hr class="footnotes-sep">
<section class="footnotes">
<ol class="footnotes-list">
<li id="fn1" class="footnote-item"><p><a href="https://en.wikipedia.org/wiki/Principle_of_charity">Principle of charity</a> <a href="#fnref1" class="footnote-backref">&#x21A9;&#xFE0E;</a></p>
</li>
<li id="fn2" class="footnote-item"><p>I have to say that smart kids will tend to do that very often. Assuming that they already know everything, and they already get it. Part of this practice is to discipline this behavior. And more importantly, it is to remind those smart kids that there is always more that they do not know. NO ONE knows everything. <a href="#fnref2" class="footnote-backref">&#x21A9;&#xFE0E;</a></p>
</li>
<li id="fn3" class="footnote-item"><p>Seems like I need to expand on this after some feedback from colleagues and friends. It is difficult to precisely define the threshold. Some people might interpret openionated as asshole-ness, others might see directness and vocal as asshole-ness. The point is that some combination of these qualities is needed and is good. The challenge, for me, would be to manage these qualities to achieve the right balance. <a href="#fnref3" class="footnote-backref">&#x21A9;&#xFE0E;</a></p>
</li>
</ol>
</section>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Thoughts on Technical Interviews]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>I had previously written an internal memo about how I conduct technical interviews at Zid, which has sparked an interesting discussion. There, I mentioned that, in technical interviews, I do not necessarily specifically look for technical knowledge &#x2014; that is, how much the candidate already knows. Instead, I look for</p>]]></description><link>https://hussam.engineering/thoughts-on-technical-interviews/</link><guid isPermaLink="false">64f21aa116231504c3c42f81</guid><dc:creator><![CDATA[Hussam Almarzooq]]></dc:creator><pubDate>Sat, 09 Sep 2023 06:38:28 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>I had previously written an internal memo about how I conduct technical interviews at Zid, which has sparked an interesting discussion. There, I mentioned that, in technical interviews, I do not necessarily specifically look for technical knowledge &#x2014; that is, how much the candidate already knows. Instead, I look for how they would interact in a knowledge exchange setting. In other words, how would they react if someone taught them something or they taught someone something? How would they behave in a setting where they are the least knowledgeable person or the most knowledgeable person?</p>
<p>Here, I will expand on this thought and discuss what I would learn about a candidate depending on how they react during the interview. Keep in mind that the interview setting can affect the candidate, but I think given how informal we, at Zid, try to make them, this effect is negligible.</p>
<p>Now, in the interview, we do a design exercise where I present the candidate with a product idea and some constraints and ask them to guide me through their thought process to build such a system. Here, we get to our first road crossing. Some engineers would be paralyzed and ask me to guide them. Usually, their first question is: What do you mean? They want a set of well-defined questions for them to answer and not an open-ended prompt. These engineers are executor engineers. The team lead<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup> gives them clear instructions of what they want, and they execute<sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>. Other people would start answering immediately. They do not ask any clarifying questions. They just go! These would usually mess up and go all over the place, and I would have to stop them to reorient. The last group are ones who would explicitly state their assumptions (or ask for ones). They wouldn&apos;t start the design without ensuring they understand what they are getting into.</p>
<p>The candidate&apos;s attitude towards this exercise gives a glimpse of their leadership style and attitude. Some people will never make a decision on their own. They might come up with options but never decide. Others would recklessly make decisions. And there are those who make well-thought-out moves.</p>
<p>Also, this demonstrates how independent the candidate is. There is a difference between asking to understand the context to make a well-informed decision and asking to push the interlocutor to make the decision on your behalf. There is a balance to strike between assumption-making (based on context) and asking questions to be independent yet not naively reckless. If someone asks too many questions, they require too much babysitting. And if someone asks no questions, they do not know what they do not know. To be honest, I&apos;ve yet to decide which one is worse.</p>
<p>Returning to the interview, we get to the more technical parts as we go through the discussion. This is where I get to assess the candidate&apos;s technical depth. In the previous parts, we mostly learned about their mindset and attitude. Here, I would ask the candidate to concretely specify how they would implement this system.</p>
<p>They should choose the programming language, framework, and data stores (yes, plural). The majority of people would suggest the stuff that they already know. They would choose the programming language and framework they are familiar with and one of the major relational databases (i.e., Postgresql or MySQL). A small minority would be adventurous and suggest tools they have never used but heard might be appropriate for their case.</p>
<p>To assess their understanding, I would do the following: If they chose the tools they know, I would suggest to them a different tool that could work just as well (or better) and see how they differ &#x2013; if at all. Some would say they simply do not know, some would make stuff up to defend their original stand, and some would discuss how this suggestion would or wouldn&apos;t work, given the system constraints addressed earlier.</p>
<p>On the other hand, if they were adventurous, I would point out a known problem with the technology they chose to see if they really have a technical logic behind the choice or if it is just chasing the buzzwords. For example, I once interviewed an engineer who solved a query performance issue with Elasticsearch on the explore page of their app &#x2013; note that this app did not have search functionality. I asked them why they did not use their existing Postgresql database with a few materialized<sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>. They said they did not think about this, which says much about this person&apos;s understanding of databases. If an engineer jumps to a new tool without fully exploiting their current ones, it indicates the depth they would take in solving problems (e.g., performance regressions are a very common example).</p>
<p>Circling back to my original point, all of these discussions should be viewed as knowledge exchange settings. Presenting a design is knowledge exchange, objecting to it is knowledge exchange, and defending it is knowledge exchange. Evaluating a knowledge worker should be based on this measure and not purely on what they already know. Nobody knows everything, and they are bound to learn something from someone. And at the same time, everyone will have to teach someone something at some point. So, their attitudes in these conditions will make all the difference in their success with the team and the organization.</p>
<hr class="footnotes-sep">
<section class="footnotes">
<ol class="footnotes-list">
<li id="fn1" class="footnote-item"><p>Whether product manager, project manager, or lead engineer. <a href="#fnref1" class="footnote-backref">&#x21A9;&#xFE0E;</a></p>
</li>
<li id="fn2" class="footnote-item"><p>Here, I am not talking about the quality of execution. It could be good or bad. That is not the point of this classification. <a href="#fnref2" class="footnote-backref">&#x21A9;&#xFE0E;</a></p>
</li>
<li id="fn3" class="footnote-item"><p>Assuming that they had the proper indexes in place and it did not solve it. <a href="#fnref3" class="footnote-backref">&#x21A9;&#xFE0E;</a></p>
</li>
</ol>
</section>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Unimpressive Simplicity Is Quite Impressive]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>I very often receive surprised-discontent faces from fellow engineers after I show them potential solutions to some highly contended problem in the team. It always turns out to be that they are not necessarily not convinced that this would solve the problem but rather surprised that the solution is simple.</p>]]></description><link>https://hussam.engineering/unimpressive-simplicity-is-quite-impressive/</link><guid isPermaLink="false">649d72d975898304da2a8124</guid><dc:creator><![CDATA[Hussam Almarzooq]]></dc:creator><pubDate>Sat, 01 Jul 2023 07:53:12 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>I very often receive surprised-discontent faces from fellow engineers after I show them potential solutions to some highly contended problem in the team. It always turns out to be that they are not necessarily not convinced that this would solve the problem but rather surprised that the solution is simple. Their intuition tells them that simple means easy, and the problem at hand cannot be easy. Our engineering training that hard problems require complex solutions and have to involve a bunch of design patterns has made it difficult for our minds to accept a solution that does not utilize a few dozen concepts.</p>
<p>First, we have to understand, once again, that simplicity does not equal difficulty and that simplicity IS hard. Getting to a simple solution requires multiple iterations over multiple complex solutions and digging deeper into the problem to simplify the design. The more you understand the problem, the simpler your solution will be. It goes the other way, too. Usually, when I see a very complex solution, it indicates that this engineer does not understand the problem well enough.</p>
<p>I should define my view of simplicity for the purposes of this discussion. A simple system is a system that can be understood by anyone with minimal domain knowledge<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>. In my opinion, more engineering is less business<sup class="footnote-ref"><a href="#fn2" id="fnref2">[2]</a></sup>. If you start from the technical aspect, you cloud your view of the business aspect. Why? Because you will try to look at the business from the lens of design patterns and SQL schemas. You will be trying to forcefully fit the business into the boxes of the few design patterns you have present mentally, which tends to complicate things, and not get the boxes and try to fit the use cases in them.</p>
<p>On the topic of design patterns, the most crucial part of design patterns is the patterns, not the design. Design patterns are solutions for common problems and not common solutions for problems. You need to find the problem first and then use the design. It is all about finding problem pattern <sup class="footnote-ref"><a href="#fn3" id="fnref3">[3]</a></sup>. I usually refrain from teaching design patterns to beginners (or even mid-level engineers) and prefer to teach them to come up with similar solutions on their own, which helps them to set up their mindsets to start with the problem, not the solution.</p>
<p>Despite this rant about design patterns and simplicity, why are engineers not satisfied with a simple design when someone else comes up with one? I call this the dilemma of pride and engineering complexity. It would not sound as cool to say I solved a problem with a few if statements in a Python file compared to saying I solved it by implementing an event-driven system in half a dozen micro-services.</p>
<p>However, on the contrary, it is more challenging to come up with a simple solution. It is always much easier to complicate things. A little misunderstanding here and there can go a long way in complicating your design. Simple designs are what we should be proud of because unimpressive simplicity is quite impressive.</p>
<hr class="footnotes-sep">
<section class="footnotes">
<ol class="footnotes-list">
<li id="fn1" class="footnote-item"><p>I previously discussed this in detail here: <a href="https://hussam.engineering/thoughts-on-software-design/#simplicity">https://hussam.engineering/thoughts-on-software-design/#simplicity</a> <a href="#fnref1" class="footnote-backref">&#x21A9;&#xFE0E;</a></p>
</li>
<li id="fn2" class="footnote-item"><p>This also holds true when your problem is an engineering problem. The difference would be that &quot;business&quot; is going to refer to the challenge at hand (e.g., performance, scale, cost, etc.), and &quot;engineering&quot; would be the relevant, yet not central, technical details. <a href="#fnref2" class="footnote-backref">&#x21A9;&#xFE0E;</a></p>
</li>
<li id="fn3" class="footnote-item"><p>I recommend watching Brandon Rhodes&apos;s talk on <a href="https://www.youtube.com/watch?v=pGq7Cr2ekVM">Classic Design Patterns: Where Are They Now</a>, where he gives a few examples of problems that could be solved with design patterns, but they are totally unnecessary. <a href="#fnref3" class="footnote-backref">&#x21A9;&#xFE0E;</a></p>
</li>
</ol>
</section>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Philosophy-Induced Software Engineering: How Philosophy Can Make You a Better Engineer]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Philosophy and engineering are two subjects that can be viewed as two different worlds. Unrelated and of no use to each other. Philosophy is theoretical in its investigation, and engineering is an extremely practical discipline. In this write-up, I claim that these two fields have more in common than one</p>]]></description><link>https://hussam.engineering/philosophy-induced-software-engineering-how-philosophy-can-make-you-a-better-engineer/</link><guid isPermaLink="false">641f6c0b75898304da2a80da</guid><dc:creator><![CDATA[Hussam Almarzooq]]></dc:creator><pubDate>Sat, 10 Jun 2023 11:11:19 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Philosophy and engineering are two subjects that can be viewed as two different worlds. Unrelated and of no use to each other. Philosophy is theoretical in its investigation, and engineering is an extremely practical discipline. In this write-up, I claim that these two fields have more in common than one might think and that learning philosophy helps make an engineer more thorough.</p>
<p>Note that I will usually use the term <em>engineer</em> to refer to a <em>software engineer</em>. Although, the first and weaker claim can be extended to most engineering disciplines.</p>
<h3 id="what-do-i-mean-by-philosophy">What Do I Mean by Philosophy?</h3>
<p>Philosophy can mean different things to different people &#x2013; for more on this, refer to the first section in my write-up on <a href="https://hussam.engineering/philosophy-and-programmers-why-and-how-it-makes-sense/#what-is-philosophy">philosophy and programmers</a>. For our purposes, we will distinguish between philosophy as a discipline and philosophical thinking as the thought process involved in tackling problems. Philosophy as a discipline or a field of study has different branches that examine knowledge, language, logic, ethics, and science. Philosophical thinking is the general capacity to ponder and examine things in a systematic approach. I may use the term <em>philosophy</em> to refer to <em>philosophical thinking</em> in some sections of this discussion.</p>
<h3 id="okay-youre-saying-logic">Okay, You&apos;re Saying Logic</h3>
<p>Engineers are trained to be analytical and logical in their thinking. I took logic in school and did plenty of math. Yet, that is not enough because, in engineering, there is always a system that you work within. It is unusual to challenge that system &#x2013; that does happen and may lead to great outcomes, but rather rare.</p>
<p>In philosophy, however, it is almost always about challenging the system, if you had one at all.</p>
<h3 id="philosophy-is-a-logic-exercise">Philosophy Is a Logic Exercise</h3>
<p>Philosophy discusses difficult and seemly unsolvable questions in almost every area of knowledge. Questions that challenge preconceived notions and challenge &quot;common sense&quot; lead people to come up with all kinds of creative solutions. Sometimes the solution is not to answer the question but to redefine the question.</p>
<p>One philosophical problem that we can draw direct parallels to engineering is the barber&apos;s paradox. Barber&apos;s paradox asks us to imagine a town with a law that requires all men to be cleanly shaved. And says that the barber shaves only the men that do not shave themselves. In other words, all the men who do not shave themselves have to go to the barber. Otherwise, they will have to shave themselves. The question here is, who shaves the barber? If the barber shaves himself, then he does not go to the barber (that is himself). And if the barber does not shave himself, then he has to go to the barber (also himself). Both cases are contradictions.</p>
<p>Ignoring the mathematical roots of the paradox, this puzzle shows an example of a system design that sets the conditions you will have to operate in, which may make some tasks impossible to accomplish. The constraints that our system had set made finding the &quot;shaving behavior&quot; of the barber non-deterministic. It could be any of the two conditions; it is up to the implementer. Imagine having a language spec with a similar constraint, and it is up to the compiler maintainers to choose a behavior &#x2013; we have many examples of this with the C compilers.</p>
<p>You might say this is a mathematical puzzle, not really philosophy, so I will give another example. However, this time I will take the other direction. I will give an example in engineering that maps to philosophy. This time, I will talk about my approach to debugging. I call it skeptic debugging.</p>
<p>Skeptic debugging is to assume that nothing is working in your application and then start building your confidence layer by layer, which means if I start by assuming that my application is broken to the point where it fails to boot. I start by ensuring it boots, then confirm that I can navigate to the page where the faulty feature is, and so on, until I find the point where my expectations are not met.</p>
<p>Skeptic debugging is a bottom-up approach as opposed to the top-down approach where you would jump into where the feature is and try to mess around until you get sick of it and then realize that nothing is wrong here, but it is the layer below that is broken.</p>
<p>I think skeptic debugging, as the name implies, directly maps to the skepticism (precisely, cartesian skepticism) in the philosophy of knowledge or epistemology. That is the process of validating one&apos;s beliefs, starting from the most foundational beliefs one has.</p>
<h3 id="philosophy-directly-influences-engineering-decisions">Philosophy Directly Influences Engineering Decisions</h3>
<p>Some fields of philosophy have immediate implications on how we approach engineering, and in some cases, philosophy might obligate you not to do engineering &#x2013; as in some ethical dilemmas.</p>
<p>Let us start with ethics. Ethics (or moral philosophy) is the study of what makes an act right or wrong and how we decide what is valuable. Value theory studies the why, how, and to what extent humans should value anything (including a person, a concept, or an object). This is crucial when designing any kind of autonomous robots, especially autonomous vehicles where we have to design robots to make decisions in some variants of the <a href="https://en.wikipedia.org/wiki/Trolley_problem">trolly problems</a>. In the trolly problem, the vehicle is the agent having to decide whether, for example, to prioritize the safety of its passengers, another vehicle&apos;s, or the pedestrians&apos;.</p>
<p>Another example is the philosophy of language. Philosophy of language studies how language is used and how it is understood. This is the root of natural language processing in computing. Natural language processing (NLP) tries to understand the user&apos;s language and tries to produce utterances in response. NLP specialists have been trying to model human languages in structures that computers could then interpret, attempts that have followed language philosophers like Chomsky&apos;s Syntactic Structures. There is also a paper titled <a href="https://www.researchgate.net/publication/363354452_Wittgenstein%27s_Philosophy_of_Language_The_Philosophical_Origins_of_Modern_NLP_Thinking"><em>Wittgenstein&#x2019;s Philosophy of Language The Philosophical Origins of Modern NLP Thinking</em></a>, that I think is a great resource to get a further picture of how philosophical understanding of human language has influenced computational understanding.</p>
<h3 id="how-do-i-start">How Do I Start?</h3>
<p>Philosophy is a skill that can be acquired by reading a few books or even dozens of them. Philosophy&apos;s core values are acquired through challenging the ideas. Take an idea, and try to understand it very well to the point where you know where it fails to achieve a certain goal and object to it. You may ultimately agree with the end result, but you can object to the reasoning that led to this result. It is all about learning to grasp ideas and challenge them systematically.</p>
<p>One approach that I think is helpful to get started is to find a reading partner or, better yet, partners &#x2013; the optimal size is 3 to 5 at most. You decide on a reading passage as a group, read it, and then come prepared to explain it to each other and critique it. It may be helpful at the beginning for each one to research how other philosophers have objected to the selected passage. For some more difficult passages, you may also need to research how others have interpreted the text to aid you in understanding it before heading to the discussion.</p>
<p>Now, the question is, where do we get these topics and passages from? I recommend starting with a history of philosophy book<sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup>. These books usually provide an overview of the philosophical problems that have risen throughout history (usually up to the 1900s). Then you choose one of the topics to dive deeper into. But now, how do you find the texts for that topic? Once you know the topic, you can look for college courses&apos; syllabi. Colleges post these publically and would usually include the readings for the course.</p>
<hr class="footnotes-sep">
<section class="footnotes">
<ol class="footnotes-list">
<li id="fn1" class="footnote-item"><p>If you like novels, I would recommend reading <em>Sophie&apos;s World</em> by Jostein Gaarder. For a non-fictional introduction to philosophy, I recommend <em>What Does It All Mean?</em> by Thomas Nagel and <em>Think: A Compelling Introduction to Philosophy</em> by Simon Blackburn <a href="#fnref1" class="footnote-backref">&#x21A9;&#xFE0E;</a></p>
</li>
</ol>
</section>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Thoughts on Designing Software Abstractions]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>&quot;All problems in computer science can be solved by another level of indirection&quot; (Butler Lampson).</p>
<p>I think this quote clearly presents how crucial abstractions are as a tool for solving computer problems. However, as important as this topic is, we do not see proper attention drawn to it</p>]]></description><link>https://hussam.engineering/thoughts-on-designing-software-abstractions/</link><guid isPermaLink="false">6457e6bf75898304da2a80ea</guid><dc:creator><![CDATA[Hussam Almarzooq]]></dc:creator><pubDate>Sun, 14 May 2023 13:43:06 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>&quot;All problems in computer science can be solved by another level of indirection&quot; (Butler Lampson).</p>
<p>I think this quote clearly presents how crucial abstractions are as a tool for solving computer problems. However, as important as this topic is, we do not see proper attention drawn to it when training engineers. Yes, there are a few books (e.g., Clean Code by Robert Martin) and many blog posts, but these usually focus on the technicalities (that is, how to write the code) rather than the thinking and mindset orientation. My goal here is to have a comprehensive discussion of what the engineer should be thinking about before and during the process of designing the abstraction. In other words, in my discussion, code is just a demonstration tool rather than the subject of the discussion.</p>
<p>Before we get started, I highly recommend reading <a href="https://stripe.com/blog/payment-api-design">Stripe&apos;s post on the first 10 years of Stripe&apos;s payments APIs</a>. It tells a story of the natural progression of a software business and how its offering has grown, and so has the complexity of its abstractions. Their case is unique because their main product is APIs, so changes in the abstractions are more prominent. I will be referring to it as it has a few great examples of most of the ideas that I will discuss.</p>
<h3 id="defining-the-units-of-abstraction">Defining the Units of Abstraction</h3>
<p>By units of abstraction, I mean what we should expect our users to know and what we expect them never to have to know. This is defining the inputs and outputs of the abstraction. An example of this is Stripe&apos;s <em>PaymentIntent</em> API. They expect the API user to learn about a few Stripe objects like <em>Customer</em>, <em>PaymentMethod</em>s, and <em>PaymentIntent</em>s themselves to be able to use the API. Then, internally they abstract away the complexity of handling the API. They do not expect their API user to know how they store customer info or how they integrate with networks. And this is an intentional design decision.</p>
<p>Another example, this time from a micro perspective, is at ZidShip, we had a balance management service. The engineer working on it was suggesting to implement updating the balance as follows (written in Python and Django):</p>
<pre><code class="language-python">package = merchant.packages.select_for_update().get_active_package()
if package:
    if package.balance_remaining &lt;= 0:
        raise Exception(&quot;Insufficient package balance&quot;)
    package.balance_remaining = F(&apos;balance_remaining&apos;) - 1
    package.save()
</code></pre>
<p>This will select the active package for update, then check if it&apos;s not null, ensure it has balance, and then deduct 1 from it.</p>
<p>I had a few comments on this design. One, the user needs to know the inner workings of the package and be careful to check the balance. Two, the <code>select_for_update</code> part is easy to miss, and I am worried that one might forget to add it and cause a race condition. So, I asked the engineer to try to abstract it.</p>
<p>The engineer&apos;s intent was to make it explicit, specifically, the <code>select_for_update</code>. They wanted to make sure that the user of the packages API is aware that they are locking the row when they do it.</p>
<p>That is valid; however, they were not thinking about their units of abstraction. What this engineer had in mind was abstracting loading the packages, but what I suggested was abstracting all balance manipulation. That is, make an abstraction layer that takes the merchant and how much you want to deduct or add to their balance. In other words, we are abstracting the whole snippet as follows:</p>
<pre><code class="language-python">def deduct_balance(merchant, amount):
    package = merchant.packages.select_for_update().get_active_package()
    
    if not package:
        raise Exception(&quot;Merchant has no active packages&quot;)
    
    if package.balance_remaining &lt;= 0:
        raise Exception(&quot;Insufficient package balance&quot;)
   
    package.balance_remaining = F(&apos;balance_remaining&apos;) - amount
    package.save()
</code></pre>
<p>Now, every call site would only need to know two things, the merchant and how any units (i.e., the amount) we want to deduct. Then loading and locking the row is centralized. And we added a new case for handling merchants that do not have active packages.</p>
<p>Why is this better? First, all operations done on balance HAVE to be atomic and would require locking the row, so this way, we avoid forgetting to select for update. Second, users of the API would require so much less context to be able to interact with the balance. Meaning they will not need to learn about active and inactive packages or how these packages are purchased and activated.</p>
<p>You can see how defining the units is crucial on all levels, whether designing an API with a few hundred endpoints, like Stripe&apos;s or even implementing a 10-line function to help you avoid all sorts of nasty race conditions.</p>
<h3 id="understanding-the-dimensions-of-the-abstraction">Understanding the Dimensions of the Abstraction</h3>
<p>This is where we define what I call the depth and breadth of the abstraction. The wider the abstraction, the more use cases it has, and the deeper it is, the more layers it hides. Exploring these dimensions gives you clarity of the boundaries of your scope. Two common traps that engineers fall through are what I call <em>abstraction bloating</em> and <em>pass-through</em> abstractions.</p>
<p><em>Abstraction bloating</em> happens when you try to incorporate too many use cases into your units of abstraction. In dimensions terms, the abstraction is too wide. Going back to Stripe&apos;s example, their team tried to force their original credit card (<code>Charges</code>) abstraction to do too much. Charges API was designed to handle immediately finalized payments that did not require customer action, and then they had to break one or both of these conditions. This an example of both working on the wrong units of abstraction and bloating it at the same time.</p>
<p>Another case of bloating is Django rest framework(DRF)&apos;s serializers. Serializers in most other contexts convert objects from the language&apos;s native format to a format that can be transmitted elsewhere (e.g., python object to json). However, DRF&apos;s serializers do more than that. They handle data validation and state manipulation (i.e., <code>save</code> and <code>update</code> methods). For those who never used DRF, this is a sample serializer for a user model adapted from DRF&apos;s docs.</p>
<pre><code class="language-python">class UserSerializer(serializers.Serializer):
    email = serializers.EmailField()
    username = serializers.CharField(max_length=100)
    
    def create(self, validated_data):
        return User(**validated_data)

    def update(self, instance, validated_data):
      ...
</code></pre>
<p>This converts <code>User</code> objects to json, validates and parses json into <code>User</code> objects, and manages the state in the <code>create</code> and <code>update</code> methods. In this design, the serializers do too much and can be confusing and hard to learn. Yet, some might argue that this abstraction is appropriate. The DRF maintainers were thinking about making fully functional units that, once mastered, could be used to quickly implement the majority of use cases. I will be discussing this idea further in the next section.</p>
<p>A <em>pass-through</em> abstraction is an abstraction that does not hide any complexity. It assumes too much knowledge from its user. It occurs when you do not consider the depth of the abstraction. You may recognize this as &quot;shallow abstraction,&quot; but I will refer to it as pass-through for my purposes. A very famous example of this is the Java I/O library. I think most of us had to use this library at some point. I can never write any I/O code in Java from my memory. The library exposes too much complexity to the developer. For example, to read a file, you must know the difference between a <code>File</code>, <code>FileDescriptor</code>, <code>FileReader</code>, <code>BufferedReader</code>, and a <code>Scanner</code>. The library has different sets of abstractions for <em>parsing</em> versus <em>reading</em> a file, even though parsing can be a subset of reading. With this many concepts, it would not be much different from calling the lower-level APIs directly. Hence, a pass-through abstraction.</p>
<h3 id="start-and-end-with-the-user-in-mind">Start and End With the User in Mind</h3>
<p>It is easy to get caught up in the implementation of the abstraction and forget the user. This is where the abstraction starts leaking. Ideally, the designer of the abstraction should think from the user&apos;s perspective. Think about the common use cases and how concentrated or scattered they are. Always make the most common use case the easiest.</p>
<p>I usually do this by laying out the use cases, starting with the simplest (not necessarily the most common) and then expanding the design based on what needs to be changed for each new use case. For example, let&apos;s say I am writing a function to upload files to S3. The files can be either public or private. Private files can be accessed through signed urls that have an expiration date.</p>
<p>I would do this in three phases. First, consider the simplest case, which is uploading a public file. Then, add support for private files. Lastly, add support for signed urls. So, our interface for the first case would be something like this:</p>
<pre><code class="language-python">def upload_to_s3(file: IO, key: str):
		pass
</code></pre>
<p>We take any IO-compatible object and the key (the path in S3 terms). Then, we expand to support private files by simply adding an <code>is_private</code> parameter like so:</p>
<pre><code class="language-python">def upload_to_s3(file: IO, key: str, is_private=True):
		pass
</code></pre>
<p>Notice that I am setting the default to <code>True</code> because it turns out that, in most cases, we need to upload the files not to be publicly viewable. And we do not want our engineers to accidentally upload a file publicly, so we make that use case the default. We are making uploading a public file an active choice.</p>
<p>Now, we want to support signed url generation. So, we add two new parameters for that, <code>generate_link</code>, and <code>link_expiration</code>. <code>generate_link</code> decides whether or not to generate the link and <code>link_expiration</code> specifics how long (in seconds) this link should be available before it expires.</p>
<pre><code class="language-python">def upload_to_s3(
		file: IO,
		key: str,
		is_private=True,
		generate_link=False,
		link_expiration=604800,
):
		pass
</code></pre>
<p>Again, we are adding defaults to keep the most common case the easiest. So by default, we will not generate a link and will ignore the expiration parameter. And if the <code>generate_link</code> parameter is enabled, we will generate a link with the specified expiration.</p>
<p>Now, notice how we are hiding all complexity of S3 and which buckets are used for which files. There are no S3-specific parameters passed to the function. We can even swap S3 for a different storage backend, for that matter. These are all part of the abstraction. Users of this function who need to upload a private file with a signed link should not have to actively think about which bucket they are uploading to and what permissions should be assigned to the file. These are all implementation details and do not contribute to the user&apos;s business value.</p>
<p>Going back to the Java example, I think the Java IO library fails to make the most common use case easy. It focuses so much on purity rather than usability. The most common use case there is reading a local file as a whole or line by line. And for the majority of these cases, using a buffered reader is most optimal. So, these should be our defaults. I would leave designing a better API for this in Java as an exercise for the reader.</p>
<p>And for Stripe, this is exactly what they were excelling at. They made the common case so easy to implement and start someone might even argue that this made them complicate the less common case.</p>
<h3 id="allow-your-users-to-escape-your-abstraction">Allow Your Users to Escape Your Abstraction</h3>
<p>The whole point of abstractions is to make a certain job easier. Sometimes that job is not exactly what the abstraction designer intended or assumed, which is not necessarily bad. Actually, more often than not is a good sign. The abstraction has proven useful, and its use cases have expanded.</p>
<p>The difficulty here is that designers sometimes intentionally lock down their abstractions, so they cannot be expanded. That could happen due to a few design decisions.</p>
<p>First, building the whole abstraction as one unit or layer where the user cannot use any lower levels of the abstractions to expand the use cases. Let&apos;s go back to our S3 upload example and assume that some user wants to generate signed urls for a pre-existing file. Maybe the original url has expired, or they might have never generated one in the first place. If we implemented the logic directly in <code>upload_to_s3</code>, that user would not be able to utilize our abstraction. A better approach would be to extract the link generation  logic into a separate function. Like so:</p>
<pre><code class="language-python">def upload_to_s3(
		file: IO,
		key: str,
		is_private=True,
		generate_link=False,
		link_expiration=604800,
):
	  # Upload logic
		if generate_link:
        return generate_signed_s3_url(key, link_expiration)
        
def generate_signed_s3_url(key, link_expiration=604800):
    pass
</code></pre>
<p>Yes, this seems obvious and straightforward, but I have seen this so often that I had to show such a simple case.</p>
<p>The second category of design decisions that prevent the user from escaping the abstraction is mainly caused by object-oriented programming (OOP). I believe pushing engineers to be OOP purists leads to designs like Java&apos;s IO library. One OOP that I want to focus on is member visibility. For 90% of the cases, all class members should be public. I would go as far as to say that static analysis tools should have a check for preventing engineers from hiding class members. The reason is that it is common to find a method that does almost the thing you want, but not exactly, so I would copy the method&apos;s implementation and change what I need elsewhere. However, I cannot do that if all the members that the method uses are hidden (either private or protected).</p>
<p>One might say that &quot;Just make a subclass and override it.&quot; Well, one, that is a new pass-through abstraction. Two, that is not doable if the method I am overriding is private. And three, it is very common that you are trying to debug something in the shell (or maybe running a one-time script), and you want to go through some method and run it line by line, but you cannot do that because members are hidden.</p>
<p>I can go on for hours with more reasons why defaulting to hiding members is a bad practice, but I will stop here. The only case where I think hidden class members make sense is in systems programming, where you want to deliberately prevent the abstraction users from tampering with some state or a resource because that could lead to a series of undefined behaviors. Yet, even then, I would carefully decide what should be hidden and try to leave as much of the interface public as possible.</p>
<h3 id="closing-remarks">Closing Remarks</h3>
<p>I am only scratching the surface of the considerations that go into designing abstractions. There are many other tradeoffs that should be considered, including <a href="https://hussam.engineering/the-software-design-questions/">the software design questions</a> that I have previously introduced in a separate post.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Applying The Software Design Questions Approach]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>In the <a href="https://hussam.engineering/the-software-design-questions/">previous article</a>, I introduced the software design questions and gave some scattered examples of the various questions. In this article, I will present a fictional business and try to apply the questions on it as a practical example.</p>
<p>Before I begin, I would like to thank <a href="https://sa.linkedin.com/in/atheer-alfawaz-189304156">Atheer Alfawaz</a></p>]]></description><link>https://hussam.engineering/applying-the-software-design-questions-approach/</link><guid isPermaLink="false">636e584375898304da2a80ba</guid><dc:creator><![CDATA[Hussam Almarzooq]]></dc:creator><pubDate>Sat, 17 Dec 2022 17:45:56 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>In the <a href="https://hussam.engineering/the-software-design-questions/">previous article</a>, I introduced the software design questions and gave some scattered examples of the various questions. In this article, I will present a fictional business and try to apply the questions on it as a practical example.</p>
<p>Before I begin, I would like to thank <a href="https://sa.linkedin.com/in/atheer-alfawaz-189304156">Atheer Alfawaz</a> for her suggestion of the business idea and for workshopping this post with me.</p>
<p>Our business is going to be a gig economy-based vehicle services platform. We will expand on the details in the next section.</p>
<h3 id="the-business-context">The Business Context</h3>
<p>Here, we will answer the following four questions: What do we do? Who are we serving? What is our goal? How are we trying to reach it?</p>
<p><strong>What do we do?</strong> We connect service providers to customers.</p>
<p><strong>Who are we serving?</strong>  For our first iteration of the products, we will only provide non-urgent car services (e.g., car wash, oil change, etc.). So, the assumption is that our customers are going to be med-to-high-income car owners, and they will book beforehand.</p>
<p><strong>What is our goal?</strong> To be an all-in-one vehicle services platform.</p>
<p><strong>How are we trying to reach it?</strong> Our first iteration will focus on providing non-urgent car services like car wash and changing engine oil. We will have two types of services: at-home and in-store.</p>
<h3 id="design-cycle">Design Cycle</h3>
<p>Before I start proposing designs, I want to explain our procedure to doing that. I call this procedure: the sketch, mock, evaluate, enhance, and iterate cycle.</p>
<p>In this sketching step, we will write down our current conception of the system &#x2013; we will specifically focus on the data model. Then, the mocking steps involve testing our design against assumed business use cases (including near-future ones). Then, we evaluate if this design is good enough or lacking. Also, this is where we evaluate the complexity of our design. If it is lacking or too complex, we will proceed to the enhancement step. Here, we may add to our design, remove something from our design, or even start from scratch. And we start over.</p>
<h3 id="first-iteration">First iteration</h3>
<p>Our main entities are going to be <em>Provider</em>, <em>Customer</em>, <em>Service</em>, and <em>Request</em>.</p>
<h4 id="sketch">Sketch</h4>
<p>So, my first iteration of the data model would be as follows:</p>
<ul>
<li>
<p>Service:</p>
<ul>
<li>Name</li>
<li>Category</li>
<li>Description</li>
<li><strong>Price?  &lt;&#x2013; Keep this one in mind</strong></li>
</ul>
</li>
<li>
<p>Provider:</p>
<ul>
<li>Name</li>
<li>Description</li>
<li>Services (from the Service entity)
<ul>
<li>type: in-store/at-home</li>
<li><strong>Price?</strong></li>
</ul>
</li>
</ul>
</li>
<li>
<p>Customer:</p>
<ul>
<li>Name</li>
<li>Phone number</li>
<li>Location</li>
</ul>
</li>
<li>
<p>Request:</p>
<ul>
<li>Service</li>
<li>Provider</li>
</ul>
</li>
</ul>
<p>Now, we have a question that we need to ask ourselves. Should the price be set per service or per provider per service? And this is where we go back to discussing with the product managers to make our first tradeoff. Are we letting providers set their own prices, or are we setting one price for each service? If we were to let the providers set their own prices, what would we do when we get to on-demand services (remember, our first iteration is non-urgent services)? We ask that because our vision is to become an all-in-one vehicle services platform, so we need to ensure that we do not shoot ourselves in the foot. This is a tradeoff between flexibility and consistency.</p>
<p>Also, from a business perspective: if providers got used to being able to set their own prices, would later enforcing a fixed price be perceived positively?</p>
<p>Let&apos;s say we decided to let them set their prices, and this marks our first baking assumption. Our design would be like so:</p>
<ul>
<li>
<p>Service:</p>
<ul>
<li>Name</li>
<li>Category</li>
<li>Description</li>
</ul>
</li>
<li>
<p>Provider:</p>
<ul>
<li>Name</li>
<li>Description</li>
<li>Services (from the Service entity)
<ul>
<li>type: in-store/at-home</li>
<li>Price</li>
</ul>
</li>
</ul>
</li>
<li>
<p>Customer:</p>
<ul>
<li>Name</li>
<li>Phone number</li>
<li>Location</li>
</ul>
</li>
<li>
<p>Request:</p>
<ul>
<li>Service</li>
<li>Provider</li>
</ul>
</li>
</ul>
<h4 id="mock">Mock</h4>
<p>We have the following business questions inferred from the assumed UI and flow:</p>
<ul>
<li>What are the available providers of a certain service at home?</li>
<li>Who are the busy providers?</li>
</ul>
<h4 id="evaluate">Evaluate</h4>
<p>Now, does this design answer our mock questions? For the first one, we can filter the list of services at-home and list the providers. That could work, but &quot;available&quot; means that they are not busy at the moment. Meaning, we need to tell if they are busy and remove them from the available list. Our current design does not support that. And that is also the same issue we will face with the second question.</p>
<h4 id="enhance">Enhance</h4>
<p>To fix our issue, we need to add two things a booking time and a status. Status can be: initiated, in progress, completed, or canceled. So the Request would look something like this:</p>
<p>Request:</p>
<ul>
<li>Service</li>
<li>Provider</li>
<li>Status</li>
<li>Booking Time</li>
</ul>
<p>But if we read a little bit into the future. We realize that we can tell who is busy but not how long they would be. So, we need to also add an &quot;average&quot; booking duration in each service.</p>
<h3 id="second-iteration">Second Iteration</h3>
<p>After the three enhancements added in our last iteration, our new sketch would look as follows:</p>
<h4 id="sketch">Sketch</h4>
<ul>
<li>
<p>Service:</p>
<ul>
<li>Name</li>
<li>Category</li>
<li>Description</li>
</ul>
</li>
<li>
<p>Provider:</p>
<ul>
<li>Name</li>
<li>Description</li>
<li>Services (from the Service entity)
<ul>
<li>type: in-store/at-home</li>
<li>Price</li>
<li>Duration</li>
</ul>
</li>
</ul>
</li>
<li>
<p>Customer:</p>
<ul>
<li>Name</li>
<li>Phone number</li>
<li>Location</li>
</ul>
</li>
<li>
<p>Request:</p>
<ul>
<li>Service</li>
<li>Provider</li>
<li>Status</li>
<li>Booking Time</li>
</ul>
</li>
</ul>
<h4 id="mock">Mock</h4>
<p>We will keep our previous two questions, and we will add the following:</p>
<ul>
<li>When will a certain provider be free?</li>
<li>What are all the requests of customer <em>x</em>?</li>
</ul>
<h4 id="evaluate">Evaluate</h4>
<p>For our original two questions, we now can tell who&apos;s available based on booking times and their service type. Likewise, we can use the booking times to see who&apos;s currently busy. For the two new cases, the first question is easy. We can, again, use the booking time and add it to our average service duration. As for our last question, this is something that seems like we overlooked. Our <code>Request</code> entity does not have a customer :)</p>
<h4 id="enhance">Enhance</h4>
<p>This time we will simply link our requests to their customers by adding a customer to the Request, like so:</p>
<p>Request:</p>
<ul>
<li>Service</li>
<li>Provider</li>
<li>Customer</li>
<li>Status</li>
<li>Booking Time</li>
</ul>
<h3 id="final-design">Final Design</h3>
<ul>
<li>
<p>Service:</p>
<ul>
<li>Name</li>
<li>Category</li>
<li>Description</li>
</ul>
</li>
<li>
<p>Provider:</p>
<ul>
<li>Name</li>
<li>Description</li>
<li>Services (from the Service entity)
<ul>
<li>type: in-store/at-home</li>
<li>Price</li>
<li>Duration</li>
</ul>
</li>
</ul>
</li>
<li>
<p>Customer:</p>
<ul>
<li>Name</li>
<li>Phone number</li>
<li>Location</li>
</ul>
</li>
<li>
<p>Request:</p>
<ul>
<li>Service</li>
<li>Provider</li>
<li>Customer</li>
<li>Status</li>
<li>Booking Time</li>
</ul>
</li>
</ul>
<p>Note that it is &quot;final&quot; in the sense that we would build this, but it does not mean we will not change it. New cases may come up that would make us go back to the drawing board and start over with this design.</p>
<h3 id="some-pointers">Some Pointers</h3>
<ul>
<li>Try to keep your designs as small as possible (that usually corresponds to being simple), and add to them as needed. Removing is generally more challenging than adding.</li>
<li>Stay away from database jargon &#x2013; that is not what we are designing for at this step. Notice that I have not mentioned anything about databases. Yet, this design can mostly map nicely to your database schema.</li>
</ul>
<h3 id="exercise-questions">Exercise Questions</h3>
<p>I will add more mock questions to challenge the design and leave it to you to improve the design based on these questions.</p>
<ul>
<li>Can a provider serve multiple customers simultaneously (e.g., car wash stations)? How can we limit the number of customers?</li>
<li>What are the working hours of a provider?</li>
<li>What is the coverage of at-home services (i.e., what areas are served by a provider)?</li>
</ul>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[The Software Design Questions]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Last year, I published a collection of discussions on <a href="https://hussam.engineering/thoughts-on-software-design">software design</a> where I discussed a set of central principles that should be considered while designing software. Here, I move to a meta-discussion about what questions you should ask when trying to apply those principles. If you have not read the</p>]]></description><link>https://hussam.engineering/the-software-design-questions/</link><guid isPermaLink="false">629414f6854d6304c268ac42</guid><dc:creator><![CDATA[Hussam Almarzooq]]></dc:creator><pubDate>Wed, 29 Jun 2022 12:34:27 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Last year, I published a collection of discussions on <a href="https://hussam.engineering/thoughts-on-software-design">software design</a> where I discussed a set of central principles that should be considered while designing software. Here, I move to a meta-discussion about what questions you should ask when trying to apply those principles. If you have not read the first post, that is okay. It is not required to understand this discussion so you can read the posts in any order. You only need to know from that post the definitions of a system and software design. Software design is the process of managing tradeoffs when architecting a system. And a system in this definition can range from a single process/thread program to a multi-cluster, multi-region distributed system.</p>
<p>I want to start with what this write-up is not. This is not a step-by-step manual on how to design scalable and simple systems. I believe that software design is part knowledge, part art, and part experience. I also want to emphasize that no matter how good the design is, it is bound to have some shortcomings. So, the goal is not to eliminate those shortcomings but rather to minimize their blast radius when we run into them.</p>
<p>This write-up is not going to answer questions but instead going to raise many of them. It is meant to help a designer ask the right questions to help them make more educated tradeoffs. Remember, software design is all about making tradeoffs, and making tradeoffs is all about asking the right questions.</p>
<p>I will discuss high-level and low-level ideas simultaneously, so some sections may be purely conceptual, and others may contain code snippets (in Python).</p>
<h3 id="learning-the-context">Learning the context</h3>
<p>The first step in every software design project is to learn the context in which you are working. I have discussed this more thoroughly in a <a href="https://hussam.engineering/being-your-customer-developer-edition/">previous post</a>, but I will summarize it in four main questions and expand a little: What do we do? Who are we serving? What is our goal? How are we trying to reach it?</p>
<p>The first question tells you the business line. So, for example, what we do at Zid is e-commerce. Now, the specifics of that come out of the following three questions.</p>
<p>The second question tells you who your customers are. This is where you specify your customer persona or personas. Keep in mind that if your answer to this question is &quot;everyone,&quot; it means that you are doing something wrong. If you do not know who your customer is, assume one, do not try to serve all humans all at once.</p>
<p>The third question directs your efforts. It is the &quot;mission&quot; of your business. Answering it should tell you what benefits your customers are gaining from your business, which sets your compass for what solutions you should be looking at. For example, when Zid started, we used to say that our goal was to provide an all-in-one e-commerce platform. However, we now believe that we empower retailers to grow their businesses. That change gave us a dramatically different answer to the next question, as I will show with Uber.</p>
<p>The last question dives into details of your offering, and it builds on your answer to the previous question. Answering this question will determine the kind of solution you will build. For example, Uber&apos;s mission is  <a href="https://www.uber.com/us/en/about/">&quot;to help people go anywhere and get anything,&quot;</a> they achieve that by offering a ride-hailing service and an on-demand delivery service. Previously, it was &quot;make transportation as reliable as running water, everywhere, for everyone,&quot; which primarily focused on ride-hailing, and that is it. Now, Uber shows you all kinds of transportation methods buses, bikes, scooters, and cars (including rentals).</p>
<h3 id="the-two-most-important-questions-in-software-design">The two most important questions in software design</h3>
<p>After learning the business context and background, we can now ask the core questions for our specific project. What are we optimizing for? What can we sacrifice?</p>
<p>We can optimize and sacrifice all sorts of values like performance, time to market, scalability, extensibility, user experience, developer velocity, developer experience, etc.</p>
<p>Any project should have some combination of these. Things you are optimizing for and things you can sacrifice. This decision should be intentional and explicit. For instance, if you are optimizing for time to market, you will have to sacrifice either performance or code quality &#x2013; maybe a combination of both.</p>
<p>Some sacrifices can be unintentional (or, in a sense, incidental or byproducts), but optimizations cannot be (otherwise, we do not know what we&apos;re doing). For example, say you are sacrificing code quality (maybe, you are planning a rewrite if this takes off). You may have to sacrifice performance in the same places because the cleaner version is also more performant, but it will take longer to build.</p>
<p>Priorities do change, so you need to be prepared. Do not bake your assumptions into too many levels of your system.</p>
<pre><code class="language-python">class InvalidPlan(ValidationError):
  pass

def validate_plan(user: dict):
	if user[&quot;plan&quot;] != &quot;pro&quot;
  	raise InvalidPlan()
  return input

def perform_billing(user: dict):
  if user[&quot;plan&quot;] != &quot;pro&quot;  # This is a code smell
  	raise InvalidPlan()
  # Do billing logic
  
def bill_user():
  validate_plan(user)
  perform_billing(user)
</code></pre>
<p>Your logic should not be too coupled with your input validation. In our example, performing billing should not check for the plan. What if you wanted to change/remove that check? You would have to run through your whole codebase. Imagine changing it in some places, but not all of them. You would start to encounter the weirdest behaviors in your system.</p>
<h4 id="note-on-sacrificing-extensibility">Note On Sacrificing Extensibility</h4>
<p>You may sometimes have to trade extensibility (for simplicity or delivery time), which means that you are making a particular assumption and sticking to it. For that, we need to understand the two kinds of assumptions: Baking assumptions and topping assumptions. Baking assumptions are the assumptions that if you were to change them, you would have to significantly rework the system. You can usually spot them when deciding whether to use a one-to-one or a one-to-many relationship. Topping assumptions are the ones that may only require a couple of lines of code to change&#x2014;for example, changing from a sequential transaction number to a random transaction number.</p>
<p>Remember: do not shoot yourself in the foot. Do not bake a contested assumption. If there is a debate on whether an assumption is the right one for the long run, avoid baking it into your design.</p>
<h3 id="what-technology-should-i-use">What technology should I use?</h3>
<p>Simply use whatever you know unless you know you shouldn&apos;t use it. This could also be seen as some sort of a tradeoff. You are optimizing for business, not technology &quot;coolness.&quot; Do you want to build a business or build something with new shiny technology? It is a decision that needs to be made.</p>
<h3 id="organizational-context">Organizational Context</h3>
<p>Here, we need to understand the team (or teams) working on our software. What is the smallest team unit size? How much freedom does this unit have?  To whom do they report? If this unit messed up, who is supposed to fix the problem? Is anyone going to be blamed? And how is your expertise distributed?</p>
<p>For example, if your teams are small (for some measure of &quot;small&quot;) and autonomous (that is, they make all kinds of product and tech decisions themselves), a micro-services architecture (yes, it only took me about a thousand words to mention it) is most probably appropriate. However, if your teams are either too large or very interconnected, a micro-services architecture will become the mega-mess architecture.</p>
<p>One case where interconnection can happen is when only a few experts have a high concentration of knowledge about the system. That means they have to participate in almost every change &#x2013; teams are not autonomous.</p>
<p>And as a side note for the previous example, this is one area where it is common for some people just to follow the trend and want to say that they implemented * insert buzzword * solution at their organization. And part of the community will be going around preaching about this architecture and how it solved their life problem, but they also need to share their organizational context (in what kind of organization was this architecture helpful). Some organizations are inherently slow, not because of technology but because of internal procedures. So implementing X architecture or X methodology will not help. It may make things worse. Remember <a href="https://en.wikipedia.org/wiki/Conway%27s_law">Conway&apos;s law</a> (yes, I had to bring that up).</p>
<h3 id="design-documentation">Design Documentation</h3>
<p>Now that we have learned our business context, specified our tradeoffs, chosen our technology, and considered our organizational context, how do we get this into writing? And how do we communicate it?</p>
<p>Unlike most of the previously introduced questions, I have a definitive answer here. You may have heard of class diagrams, use-case diagrams, sequence diagrams, etc. If you have, do not use them. These are unnecessary formalism that does not add to the conversation. On the contrary, they make it harder for people to learn all the modeling terminology and tools involved in producing those diagrams.</p>
<p>A simpler approach is to use informal diagrams and supplement them with textual descriptions with minimal usage of context-specific jargon. This way, you can onboard anyone to your documents by simply providing a glossary section that describes all the terminology used throughout the document &#x2013; no external resource needed.</p>
<h3 id="deployed-what-now">Deployed! What now?</h3>
<p>Start over. Software design is not a linear procedure. It is a feedback loop. As the software is used, we learn more about its and users&apos; behavior. Shifting our understanding of the business context and the tradeoffs we made &#x2013; taking us to the first two sections.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Philosophy and Programmers: Why and How It Makes Sense]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>&quot;Hmm, Philosophy and Computer Science. An Interesting combination&quot; is a very common response when I tell people I work in computing and am interested in philosophy. It seems like there is a perception about working in computing that a technologist is a person that would only be interested</p>]]></description><link>https://hussam.engineering/philosophy-and-programmers-why-and-how-it-makes-sense/</link><guid isPermaLink="false">61e72c32854d6304c268ac1d</guid><dc:creator><![CDATA[Hussam Almarzooq]]></dc:creator><pubDate>Sat, 22 Jan 2022 17:20:05 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>&quot;Hmm, Philosophy and Computer Science. An Interesting combination&quot; is a very common response when I tell people I work in computing and am interested in philosophy. It seems like there is a perception about working in computing that a technologist is a person that would only be interested in STEM-adjacent fields; that a very human-centered area like philosophy is not for them.</p>
<p>I believe these are but a few of many misconceptions about being in tech. They not only affect how non-technologists perceive technologists, but they also affect how technologists view themselves. In this short write-up, I will give examples of how technology (mainly software) intersects with many areas of philosophy and how beneficial it is to learn from that.</p>
<h3 id="what-is-philosophy">What is philosophy?</h3>
<p>This is an important question to answer before proceeding as philosophy can mean many things to different people. Some think of philosophy as &quot;style&quot; or the way they do things. Others think of philosophy as metaphysics or maybe how it relates to theology (and maybe to some, the antithesis of theology). Lastly, philosophy may be seen as merely the study of ethics in the most abstract and hard-to-come-by situations.</p>
<p>And these are all, more or less, parts of philosophy. Even amongst philosophers, there is this stereotypical categorization of philosophers as analytical philosophers or continental philosophers. Analytical philosophers are more interested in the rigorous math-like study, and continental philosophers focus on big picture theory.</p>
<p>Here, I use philosophy to refer to two intertwined ideas. First, I used it to refer to logic, epistemology, ethics, and the formal approach to theory formulation. Also, philosophy is the study of studying, the approach by which we examine things.</p>
<p>This specific approach to defining philosophy may not completely align with some of the academic definitions, but it serves a practical purpose here.</p>
<h3 id="logic">Logic</h3>
<p>Logic may be the easiest of the bunch to relate to technology as it is the foundation of our work. We use logic to build our software. Boolean logic is the most fundamental building block that computers are constructed on top of. Yet, most programmers are never exposed to general forms of logic. That is thinking logically outside the realm of discrete input and discrete outputs that is practiced in computational logic.</p>
<p>Deductive and inductive logics involving decomposing human utterances to make inferences and drawing conclusions are essential tools for anyone building systems. This can be seen at work all the time with product managers and business analysts. They translate people&apos;s needs specified as premises in natural language to formal definitions in a restricted language (which occasionally includes diagrams).</p>
<p>As for developers, building systems is what we do every day. Eventually, the developer will have to make decisions based on the problem statement that was not explicitly defined in the requirements document. Especially since these documents greatly vary in their depth and detail. Some details will be omitted, and the developer will have to deal with them. So, having a logic background allows you to deduce implicit requirements from the business context.</p>
<h3 id="epistemology">Epistemology</h3>
<p>Epistemology is the study of knowledge. What it means to know, believe, and the rational relation of knowledge and belief. Epistemology asks questions like: is it rational to believe in proposition x? Given that it is rational, does that belief constitute knowledge?</p>
<p>As a developer, I use epistemology whenever I am debugging a piece of code. I always tell my colleagues to be skeptics. Assume that nothing works, and then start rebuilding your confidence in what you thought was working piece by piece.</p>
<p>Epistemology also examines language and how it affects our knowledge or our perception of knowledge. Knowledge can also be context-dependent. I may know something in one context, but not in another. In that same sense, when writing code, we look into what context we have, what we know about our problem, and what we can infer. Then we structure our code to cover all of these branches of related sub-cases of the problem.</p>
<h3 id="ethics">Ethics</h3>
<p>I believe ethics has two folds in this matter. First is the study of applied ethics as a way to systematically evaluate our actions (and consequently, our software&apos;s). Second, the mode of thinking involved in studying ethics. That is having to decide what to value most and what to dismiss in evaluating decisions.</p>
<p>As previously discussed, we, programmers, love systems and patterns. The study of ethics tries to build what is called an ethical theory. That is a general system that can solve ethical dilemmas. Obviously, we are yet to develop one theory to rule them all. It is all about tradeoffs and what to prioritize. Similarly, when we design systems, we make tradeoffs. We build systems that serve certain use cases better than others because it is practically impossible to excel in all.</p>
<p>We also have ethics of computing where we discuss the moral considerations of our code. Considerations like bias, privacy, and autonomy are all at the center of such philosophical discussion. We see <a href="https://en.wikipedia.org/wiki/Algorithmic_bias">algorithmic bias</a> in <a href="https://sitn.hms.harvard.edu/flash/2020/racial-discrimination-in-face-recognition-technology/">facial recognition</a>, <a href="https://qz.com/1748321/the-role-of-goldman-sachs-algorithms-in-the-apple-credit-card-scandal/">determining credit limits</a>, and <a href="https://www.technologyreview.com/2020/07/17/1005396/predictive-policing-algorithms-racist-dismantled-machine-learning-bias-criminal-justice/">predictive policing</a>.</p>
<p>Also, ethics has some gray areas. Some are situations where the ethical theory would suggest something that the agent (the person acting) may not be as confident to do that action because of common sense. In software, we deal with unexpected behaviors. Cases where we think our algorithms should act in certain ways, but given some set of input, we get an unintended output.</p>
<h3 id="language">Language</h3>
<p>Philosophy of language studies the nature of language, the use of language, and language cognition. Such investigation can directly benefit technologists working with Natural Language Processing (NLP). NLP is especially an area of human-centric nature. You can build the models through purely statistical methods, but you need to approach the evaluation of your NLP models from a user&apos;s perspective. You need to be aware of how people use language to be able to understand and produce language.</p>
<h3 id="closing-note">Closing Note</h3>
<p>I can confidently say that Philosophy made me a better engineer and a better tech lead. Keep in mind that I only touched upon the direct parallels of philosophy and technology in this write-up. There is a lot more to gain from practicing philosophical thinking. Philosophical thinking pushes you to think of a problem in all directions and perspectives, allowing you to develop unforeseen solutions.</p>
<p>Lastly, I omitted the philosophy of science, yes this is a thing, because this field on its own can have a write-up of this size to explain how important it is to technology. It discusses the scientific methods, their effectiveness, and implications of scientific findings. And one deep area it investigates is Artificial Intelligence, the mechanization of reasoning, and regulating Artificial Intelligence.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Reflections On Abstraction: Philosophy, Art, and Computing]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>This post might be a little deviant from my usual tech-focused blogging, but I think it&apos;s still relevant to tech&#x2014;especially the abstraction section of my <a href="https://hussam.engineering/thoughts-on-software-design/">previous post</a>.</p>
<p>The concept of abstraction is all around us. We may not actively think about it, but if we put</p>]]></description><link>https://hussam.engineering/reflections-on-abstraction-philosophy-art-and-computing/</link><guid isPermaLink="false">6177018a72207b390abed539</guid><dc:creator><![CDATA[Hussam Almarzooq]]></dc:creator><pubDate>Mon, 01 Nov 2021 16:16:26 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>This post might be a little deviant from my usual tech-focused blogging, but I think it&apos;s still relevant to tech&#x2014;especially the abstraction section of my <a href="https://hussam.engineering/thoughts-on-software-design/">previous post</a>.</p>
<p>The concept of abstraction is all around us. We may not actively think about it, but if we put on our analytical lens for a day, we will be surprised by how much we deploy this concept in our thought, craft, and technology.</p>
<p>I define abstraction as the process of hiding the details or focusing on what really matters. Here, I will present a collection of reflections and examples of fields that use abstractions in multiple similar yet seemingly different ways.</p>
<h2 id="philosophy">Philosophy</h2>
<p>Abstraction is defined as the thought process wherein ideas are distanced from objects. Abstract is the opposite of concrete. If we think of a physical object like a book or chair, it is concrete. If we think of the concept of writing, this is abstract.</p>
<p>Let&apos;s take, for example, the common phrase &quot;A cat is on a mat.&quot; If I asked you to imagine that, you would probably imagine any random cat you could remember sitting on any random mat you can remember. And that is what I usually want to happen. In most cases, the breed of the cat, and the material of the mat, for example, are negligible details of the idea I am trying to convey or discuss.</p>
<p>This nature of abstraction is very powerful. It allows us to think of the same concept but with different mental depictions of it. What may make you want to say it&apos;s the same but different.</p>
<p>In logic, this is utilized to build formal systems. A formal system builds a theoretical organization of objects based on implicit relationships using a set of rules. For example, in number systems, sets are used to categorize values, and rules are used to define how values are constructed and manipulated.</p>
<h2 id="art">Art</h2>
<p>First, we need to distinguish between abstraction in art and abstract art. Abstract art is a genre of art that heavily utilizes abstraction. Think of abstraction in art as a scale rather than a binary categorization. The more abstract a piece, the less it resembles the physical world. The more abstract a piece, the less guided the viewer in understanding it. It leaves a lot of space for the viewer to interpret it on their own. And this is a critical component of abstract art.</p>
<p>But that does not mean you have to go all the way. Many paintings and sculptures try to resemble the essence of the real world but skip the parts that do not convey the scene or the emotions that the artist intends.</p>
<h2 id="computing">Computing</h2>
<p>Abstraction is one of the most fundamental concepts in computer science, and it can be divided into two broad categories. Abstracting computer concepts and abstracting business concepts. To abstract a computer concept is to hide the details of its inner workings and provide an interface to interact with it. A very common example is predicate abstraction (borrowed from Logic). You want to make, let&apos;s say, a sorting algorithm that can sort any arbitrary set of elements. And how can you make such a magical algorithm that does not know the type of its input beforehand? You make the condition on which you compare the elements in the input as part of the input. In other words, if you want to sort a set of integers in ascending order, you pass your integers to this algorithm and give it the sorting operator (the &lt; operator in this case). This way, we abstracted the details of sorting from the details of comparing the values.</p>
<p>On the other hand, business abstraction encompasses almost all of our product work that the end customer interacts with. When building any sort of a product or a feature, you are abstracting something. For example, let&apos;s say you&apos;re adding a presets feature to an audio mixer. If you press, let&apos;s say, the chamber preset, the mixer will have the proper echo, reverb, and gain levels set to simulate a chamber effect. By you adding this feature, you are abstracting the details of configuring the mixer.</p>
<h2 id="how-does-that-tie-together">How does that tie together?</h2>
<p>As we saw, the idea of hiding details and focusing on the essential concept is something that happens all the time around us. It is the foundation of fields like philosophy, art, logic, and computing. Actively looking would show us countless examples in our everyday lives.</p>
<p>Understanding which level of abstraction we are working with and thinking accordingly is a skill one needs to develop and master. Mastering this skill would allow us to become more productive and more innovative.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Thoughts on Software Design]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>This post will be my perspective on software design, a compilation of discussions and examples of different principles that we associate with software design.</p>
<p>Keep in mind that this is not meant to be a crash course in software design. If you are interested in such content, I recommend reading</p>]]></description><link>https://hussam.engineering/thoughts-on-software-design/</link><guid isPermaLink="false">610a90d772207b390abed525</guid><dc:creator><![CDATA[Hussam Almarzooq]]></dc:creator><pubDate>Wed, 04 Aug 2021 17:56:42 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>This post will be my perspective on software design, a compilation of discussions and examples of different principles that we associate with software design.</p>
<p>Keep in mind that this is not meant to be a crash course in software design. If you are interested in such content, I recommend reading  <a href="https://gist.github.com/vasanthk/485d1c25737e8e72759f">System Design by Vasa</a>, <a href="https://www.goodreads.com/en/book/show/39996759-a-philosophy-of-software-design">Philosophy Of Software Design By John Ousterhout</a>, and <a href="https://pragprog.com/titles/tpp20/the-pragmatic-programmer-20th-anniversary-edition/">The Pragmatic Programmer by David Thomas, Andrew Hunt</a>.</p>
<p>Why do we need software design? Poorly designed systems can lead to catastrophic outcomes for the business. Such as unmaintainable or hard to debug, not extendable, and not scalable systems.</p>
<p>First, let&apos;s set a definition of software design. Software design is the process of managing tradeoffs when architecting a system. A system in this definition can be anything from a single process/thread program to a multi-cluster multi-region distributed system.</p>
<h2 id="simplicity">Simplicity</h2>
<p>Simplicity is  the most important, easiest to understand, and most challenging to master principle of design. You may have heard of the KISS (keep it simple, stupid) principle. But what does it mean for a system to be &quot;simple&quot;.</p>
<p>I define a simple system as a system that can be understood by anyone with minimal domain knowledge. For example, a simple accounting system can be understood by any accountant or any developer with enough accounting background. Keep in mind that simple does not mean feature lacking. On the contrary, simplicity allows for extensibility, as we will discuss in the next section.</p>
<p>As you can infer from the definition, designing a simple system requires understanding the business domain. I <a href="https://hussam.engineering/being-your-customer-developer-edition/">previously discussed </a>how understanding the problem helps in driving the solution. Similarly, understanding the business domain helps in shaping your system&apos;s design. I would go as far as to say you can infer how much a programmer knows in their domain when reading their code. Vagueness in understanding leads to poorly written code resulting in complexity.</p>
<p>Also, you should avoid any fancy design patterns or tools. If you use a design pattern just because it sounds cool or is commonly used in a certain community, you will introduce unnecessary complexity to the system that could&apos;ve been avoided. I will share one example inspired by a real-world codebase &#x2013; this is just one out of many.</p>
<pre><code class="language-python">class Vehicle:
  	# Moves the vehicle for the specified duration and returns how far it moved in floats.
    def move(self, minutes) -&gt; float:
        ...


class Car(Vehicle):
    def drive(self, mode, seconds) -&gt; float:
        self.move(seconds / 60)
        ...


class Motorcycle(Vehicle):
    ...


def get_distance_travelled(vehicle: Vehicle) -&gt; float:
    distance_travelled = 0
    if isinstance(vehicle, Car):
        distance_travelled = vehicle.drive(&quot;sport&quot;, 120)
    elif isinstance(vehicle, Motorcycle):
        distance_travelled = vehicle.move(2)
    return distance_travelled
</code></pre>
<p>In the previous example, we had a base-type <code>Vehicle</code> that has been extended twice. And it has a method <code>move</code> that takes the duration in <code>minutes</code> and then returns a <code>float</code> of how far this vehicle moved in this duration of time. However, when the new type of Vehicle, <code>Car</code>, was made, the developer found that its concept of moving and its requirements were different than the default <code>move</code>. So, they made a new <code>drive</code> method that took a different combination of parameters.</p>
<p>Given this difference in the interface, the developer had to distinguish between vehicles in the call site and treat them differently, breaking the original premise of inheritance.</p>
<p>Because we do not have enough business context for this example, I won&apos;t propose a solution, but this shows how this confused understanding of polymorphism vs. inheritance made this inconsistent design. We would&apos;ve been better off if we did not make a base class for our vehicles to inherent and relied on <code>duck-typing</code> or <code>interfaces</code> depending on the language.</p>
<p>As you can see, simplicity is hard. It takes a lot of discipline and thought to get anywhere near a simple design.</p>
<h2 id="scalability">Scalability</h2>
<p>Scalability  can be divided into two subcategories extensibility and stability, or functional scalability and load scalability.</p>
<p>By extensibility, I mean the capacity to extend the system by adding new features or altering existing ones. When designing any program, small and large, always think of what features can be added. You do not necessarily have to think in-depth of the implementation details as much as having a vision of how to expand your feature set. Having this in the back of your mind helps you design for expansion.</p>
<p>On the other hand, stability may be the easier of the two. There are so many tutorials on improving the performance and stability of the specific framework or tool you are using. Things like eager loading vs. lazy loading in web apps, caching, JIT compiling, to name a few. However, I summarize all of these tutorials this way: do not do what you do not have to do. Most optimizations revolve around the idea of minimizing resources usage. If you load too much data, you are using too much memory and storage (and maybe network). If you loop too many times, you are using too much compute, and so on.</p>
<p>Keep in mind that the key to scalability is <code>simplicity</code>. If the system is too complex for you to comprehend, it&apos;s really hard to know what to optimize.</p>
<h2 id="design-process">Design Process</h2>
<p>Design is an iterative process. You come up with a cool idea to comply with particular constraints in your requirements, only to realize that it introduces more limitations or does not entirely cover the whole case. You then go back to the drawing board and either refine the proposed solution or start over.</p>
<p>One great way to reduce the time spent trying to implement a solution and later having to redo it is by having design workshops. Having concerned parties of a particular component meet and discuss a solution. Talk about what benefits a solution would provide or the problem it would solve, and most importantly, what constraints it introduces to the system. Also, you can invite people who do not directly deal with the system to have a set of fresh eyes to give a different perspective (this has proven to be very helpful in multiple situations).</p>
<p>Design should not be done in a vacuum. Once you get your first couple of thoughts on paper, try presenting them to others. Trying to explain an idea is the best way to scrutinize it. For example, one time, I had to design a schema for a routing system for shipments. I knew that I needed to know where the shipments are coming from, going to where, on what service type, by whom, and for how much. So, I decided to have each one of these questions solved by a database table.</p>
<p>One table defined origin cities. Another table linked the origin cities to the destination cities for specific service level named <code>destinations</code>. The last table had linked the previous table with couriers and their prices. After sketching this design on paper, I showed it to two fellow engineers on the team. We discussed how this proposed design would be used in code and debated some of the field names. For example:</p>
<pre><code class="language-text">destination.origin_city vs. destination.origin
destination.cost
...
</code></pre>
<p>Given that this destination table was the core piece of our system, we focused on its usage. After a couple of examples, both of the engineers found this confusing. Then we realized that all of this could be reduced to the concept of a <code>route</code>. One table has <code>origin</code>, <code>destination</code>, <code>service_level</code>, and <code>cost</code> fields. And we did not need to debate the naming:</p>
<pre><code>route.origin_city
route.destination_city
route.cost
...
</code></pre>
<p>That would not have been achieved without sharing and discussing the design with others, especially potential maintainers of the system.</p>
<p>The first design is probably the worst. So do not get attached to your first idea. Instead, keep challenging it until you verify that it achieves your design goals.</p>
<h2 id="abstractions">Abstractions</h2>
<p>As Butler Lampson says, &quot;All problems in computer science can be solved by another level of indirection.&quot; Abstraction (indirection) is a fundamental idea in software design. It helps you define what you are actually trying to solve. Most software products are abstractions of some sort, whether it is an intermediate piece of software used by other software (e.g., APIs) or an end application used by the end-user. We are always trying to abstract something.</p>
<p>So, this makes sense, but how do we tell if a problem should be solved by abstraction? And how do we define the scope of a given abstraction problem?</p>
<p>These two questions can be answered by answering two other questions: how common is this problem? And how wide is the range of use-cases? Let&apos;s take, for example, the problem of sending HTTP requests to external web services. How often do we send HTTP requests from our app? If we only have one instance of sending an http request in our app, we should not bother to abstract it. However, if we do have multiple instances, we should look into variance in usage. For example, are these all the same exact request used in various places? Are these requests sent to the same API provider? Are these all the same HTTP method? And so on.</p>
<p>Once we understand the broadness of our use cases, we can start designing our abstraction. Let&apos;s say I am building a Twitter client. In this case, I will be interacting heavily with the Twitter API. So, I will make an abstract approach to communicating with it. Look at the following function signature:</p>
<pre><code class="language-python">def call_twitter(endpoint: str, method: str, params: dict = None) -&gt; dict
</code></pre>
<p>The <code>call_twitter</code> takes an <code>endpoint</code> which is the path of the API I want to interact with. I should not include the host information or API version that I have abstracted away from the user. The <code>method</code> is simply the HTTP method that can be passed in lower or upper case. Lastly, the optional <code>params</code> and the return value are  <code>dict</code>s given that the Twitter API always takes and returns payload wrapped in an envelope as described <a href="https://developer.twitter.com/en/docs/twitter-api/data-dictionary/introduction">here</a>.</p>
<p>In a call site, I would do something like:</p>
<pre><code class="language-python">tweet = call_twitter(&quot;/tweets/2244994945&quot;, &quot;GET&quot;)
</code></pre>
<p>Which would return something like:</p>
<pre><code class="language-python">{
  &quot;data&quot;: {
    &quot;author_id&quot;: &quot;2244994945&quot;,
    &quot;created_at&quot;: &quot;2020-06-24T16:28:14.000Z&quot;,
    &quot;id&quot;: &quot;1275828087666679809&quot;,
    &quot;lang&quot;: &quot;en&quot;,
    &quot;possibly_sensitive&quot;: False,
    &quot;source&quot;: &quot;Twitter Web App&quot;,
    &quot;text&quot;: &quot;Learn how to create a sentiment score for your Tweets with Microsoft Azure, Python, and Twitter Developer Labs recent search functionality.\nhttps://t.co/IKM3zo6ngu&quot;
  }
}
</code></pre>
<p>You may also want to explore whether or not to unwrap the response and return the<code>data</code> field directly as you do not want to end up with <code>tweet[&quot;data&quot;]</code>all other the place.</p>
<p>With that being said, some &quot;attempts&quot; at abstractions may yield harmful results. These attempts can be divided into two categories. The first one is <code>immature abstractions</code>. That is when you try to implement an abstraction for the use case that is not well understood. These usually occur in abstractions of business logic. For example, let&apos;s say we are trying to implement a tax calculator. The naive way of doing it is to simply make a function that takes the price and multiply it with the rate.</p>
<pre><code class="language-python">TAX_RATE = 0.15
def calculate_tax(price: Decimal) -&gt; Decimal:
  return price * TAX_RATE
</code></pre>
<p>The problem with this design is that it does not account for products with different tax rates and exempted products. If we then needed to support those, we will have to take one of two approaches. The first approach is to handle the exception in the call site, which means the callers need to be smarter. The other approach is to change the function signature to either take the tax rate or the product category and decide the rate internally.</p>
<pre><code class="language-python">def calculate_tax(price: Decimal, category: str) -&gt; Decimal:
  return price * get_tax_rate(category)
</code></pre>
<p>However, this will require that we change all callers to pass this parameter. Note this may seem like a trivial example, but these small things can cause many cascading problems when you&apos;re not accounting for this missing value. My rule of thumb here is <strong>If you do not understand the domain, do not try to abstract it</strong>.</p>
<p>The second category of harmful abstractions is <code>fake abstractions</code> sometimes called empty or pass through abstractions. Look at the following example adapted from real world production codebase:</p>
<pre><code class="language-python">def update_product_quantities(order, operator):
  for order_product in order.products:
    if operator == &quot;+&quot;:
      order_product.product.quantity += order_product.quantity
    elif operator == &quot;-&quot;:
      order_product.product.quantity -= order_product.quantity
</code></pre>
<p>Then someone came and decided to abstract this function by doing the following:</p>
<pre><code class="language-python">def deduct_products_quantities(order):
  update_product_quantities(order, &quot;-&quot;)
  
def return_products_quantities(order):
  update_product_quantities(order, &quot;+&quot;)
</code></pre>
<p>As you can see, the two functions only have one line of code each. They do more harm than good, and they are not really abstracting anything. I need to memorize more terminology <code>deduct</code> and <code>return</code>, which are not even proper antonyms. I am assuming that the intention of the developer who made these function to hide the cognitive complexity of the <code>operator</code> parameter. However, they ended up introducing a similar amount of cognitive load when they introduced the new terminology.</p>
<h2 id="closing-thoughts">Closing Thoughts</h2>
<p>Software design is not at all about memorizing common architectures and sprinkling fancy design patterns but instead figuring out what to do and when. Furthermore, it is about doing the least amount of work that would give you the most mileage (remember, simple and scalable).<br>
I may have spent most of this post on abstractions, but I believe that our work as developers is all about abstractions. We apply all the previous principles when we implement abstractions. An abstraction needs to be simple and scalable, which can be achieved by having a good design process.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Being Your Customer (Developer Edition)]]></title><description><![CDATA[<h3 id="a-startup-centric-mindset">A Startup Centric Mindset</h3><p>The motivation for this post: throughout my experience working with many startups, I have rarely seen developers who think holistically about the problems they are supposed to solve. The dilemma we face here is that we sometimes work on products that we are not the immediate</p>]]></description><link>https://hussam.engineering/being-your-customer-developer-edition/</link><guid isPermaLink="false">5fed3592a613722fc3b411b3</guid><dc:creator><![CDATA[Hussam Almarzooq]]></dc:creator><pubDate>Sun, 03 Jan 2021 22:26:11 GMT</pubDate><content:encoded><![CDATA[<h3 id="a-startup-centric-mindset">A Startup Centric Mindset</h3><p>The motivation for this post: throughout my experience working with many startups, I have rarely seen developers who think holistically about the problems they are supposed to solve. The dilemma we face here is that we sometimes work on products that we are not the immediate users of, so we start making assumptions that may or may not be valid. And sometimes, our users develop habits to work around issues that exist in the product or need entirely new features that are not present in the product in the way we originally intended.</p><p>This post will discuss my approach to understanding the customers&apos; needs and feel their pain, and I will give examples from my current work at <a href="https://zid.sa">Zid</a>. Keep in mind that this is <em>my</em> approach, and it may not be as effective for everyone. Also, this is a mindset, not a recipe, meaning it takes time to get it to be part of your intuition, but having the ideas laid out would help you actively think in the following sense.</p><p><em>Note that some of these ideas may sound contradictory, which they are to some extent. You will have to make tradeoffs as needed in your situation.</em></p><h4 id="be-in-the-customer-s-shoes-but-keep-the-analytical-hat">Be In The Customer&apos;s Shoes But Keep The Analytical Hat</h4><p>As developers, we like to think of things as procedures or a series of logical steps. This mindset is what allows us to write programs out of problem statements. However, your customers are not developers, and they may not know the right questions to ask or how to find the root cause of the pain.</p><p>When talking to the customers, you need to forget all your preconceived notions about how the customers use the products or, at the very least, validate them. In other words, do not assume that the user uses the product as you designed it. They might have created new use cases or altered old ones in ways you never thought of.</p><p>When you get a feature request, your first intuition should be to ask your customers why they want this feature, and what problem does it solve. Something to keep in mind here is that you should be careful of rephrasing the feature statement instead of discussing the pain. You need to focus on the whys, not the hows, to get a deeper understanding of the issue, instead of suggestions for solutions; failing in that could lead to solving an entirely different problem.</p><p>For example, merchants at Zid asked for bulk price updates for their products. After further discussion with the merchants, it turned out that the goal is to make store-wide sales &#x2013; they wanted to edit the prices beforehand. Here, we asked: what happens when the sale ends? Is it possible to revert the changes? What happens to the product variants? How practical is this suggested approach in achieving the goal?</p><h4 id="you-know-how-to-automate-but-the-customer-knows-the-business">You Know How To Automate, But The Customer Knows The Business</h4><p>Now that you understand the problem they are facing. You can start sketching different workflows as your solution candidates. Here, you should be thinking more about which parts of their current process are causing the most headache, how you can eliminate them, what are your current parameters, and what steps are redundant.</p><p>One way to tackle these questions is to treat your process as a pipeline of inputs and outputs, where each pipe is a step. You then start removing pipes to see how that affects your flow and the needed inputs to the next step. More often than not, you find that you could either combine steps or remove them altogether because the system already has all needed inputs to execute a particular task &#x2013; meaning human intervention is redundant in the first place.</p><p>Building on our previous sales example, we came up with auto-applied coupons given that we understood the intent behind this feature request. They are coupons that are automatically applied to the cart based on specific rules (e.g., total, shipping address, particular products, etc.). This approach allowed for many more features to be built on top of it, and the requested feature is just one application of this more flexible solution (discount on any cart during the sale).</p><h4 id="do-not-write-all-the-code-at-once-no-matter-how-detailed-and-thorough-the-requirements-were-given-they-will-change">Do Not Write All The Code At Once, No Matter How Detailed And Thorough The Requirements Were Given &#x2013; They Will Change</h4><p>Part of finding the best approach to automating a task is testing it with real customers. Implementing the whole thing all at once and releasing it is the total opposite. Especially if this is a reasonably new business, you will be making a lot of assumptions with very little usage data to back them. I call this <em>premature automation</em> (automation of not well-understood operational procedure). Premature automation could force you to adjust what you already implemented in ways you did not intend, leading to unnecessary refactoring. Keep in mind that you don&#x2019;t need to refactor the code you didn&#x2019;t write.</p><p>In other words, you should write the minimal amount of code solving the most basic case of the task, then test it with a real user. Once you made sure that your have a sound and solid foundation, you can build the next layer on top of it, and so on.</p><h4 id="give-the-knobs-to-the-business-team">Give The Knobs To The Business Team</h4><p>This point may be more specific to internal automation. You want to keep yourself out of the daily operations. Changing a service&apos;s price or name should not be only possible by the developer (either through the DB or hardcoded in the codebase). Try to make your parameters easily adjustable by your operations team.</p><p>For example, at ZidShip, our team can use our backoffice to alter integrated couriers, from updating pricing models to changing messages to manipulating routing rules for each courier without going through a developer.</p><p>However, this may not always be feasible. In some cases, adding these toggles may not be worth the effort put towards it &#x2013; that is, if changes are infrequent and trivial. But again, do not underestimate the impact of having such options available.</p><h4 id="closing-thoughts">Closing thoughts</h4><p>This mindset is meant to complement the product manager&apos;s role rather than to replace it. A developer&apos;s thoughtful input into the product development cycle can significantly enhance the end user&apos;s experience. The product manager&apos;s role is to be in the company&apos;s front line when talking to the customers and getting their feedback, but the developer should still be as keen to learn about the customers. Moreover, the product manager should be mindful of trying to get the full picture to the developer.</p><p><br>Again, do not expect to master this overnight. It will take some time and effort in actively thinking of the right questions to ask to design such well-thought solutions.</p>]]></content:encoded></item></channel></rss>