<?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[My Notes]]></title><description><![CDATA[Software Development, Cloud, DevOps and co.]]></description><link>https://adnan-m.me/</link><image><url>https://adnan-m.me/favicon.png</url><title>My Notes</title><link>https://adnan-m.me/</link></image><generator>Ghost 4.4</generator><lastBuildDate>Thu, 02 Apr 2026 12:15:06 GMT</lastBuildDate><atom:link href="https://adnan-m.me/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[My Current AI Coding Workflow]]></title><description><![CDATA[We are all still figuring this out. Every developer I talk to seems to have a slightly different ritual when it comes to working with AI coding agents. This is my workflow.]]></description><link>https://adnan-m.me/my-current-ai-coding-workflow/</link><guid isPermaLink="false">696673bd85ae150519a2812c</guid><category><![CDATA[ai]]></category><category><![CDATA[agents]]></category><category><![CDATA[coding]]></category><category><![CDATA[specification]]></category><category><![CDATA[development]]></category><category><![CDATA[golang]]></category><category><![CDATA[javascript]]></category><category><![CDATA[typescript]]></category><dc:creator><![CDATA[Adnan Mujkanovic]]></dc:creator><pubDate>Tue, 13 Jan 2026 17:06:34 GMT</pubDate><media:content url="https://adnan-m.me/content/images/2026/01/werner-sevenster-JuP0ZG0UNi0-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: html--><div id="contents"></div><!--kg-card-end: html--><h2 id="intro">Intro</h2><img src="https://adnan-m.me/content/images/2026/01/werner-sevenster-JuP0ZG0UNi0-unsplash.jpg" alt="My Current AI Coding Workflow"><p>We are all still figuring this out. Every developer I talk to seems to have a slightly different ritual when it comes to working with AI coding agents. Some treat the AI like a junior intern, others like a stack overflow search bar on steroids.<br><br>I&#x2019;ve spent the last few months refining my own process. It is certainly not the &quot;best&quot; workflow out there, and it might even seem a little heavy-handed to those who prefer to just fire off a prompt and hope for the best. But I&#x2019;ve found that by front-loading the effort and treating the AI as a collaborator rather than just a code generator, I save myself a massive amount of debugging time later.<br><br>Here is the 16-step workflow I use to go from a vague idea to shipped feature.</p><h2 id="phase-1-alignment-and-interrogation">Phase 1: Alignment and Interrogation</h2><p>The biggest mistake I used to make was jumping straight to code. Now, I spend a significant amount of time just talking.</p><h3 id="1-the-spark">1. The Spark</h3><p>It starts with a feature idea. It might be messy or incomplete, but it&#x2019;s there.</p><h3 id="2-the-ask-mode">2. The &quot;Ask&quot; Mode</h3><p>I open a context with a custom &quot;Ask&quot; persona. This isn&apos;t configured to write code; it&#x2019;s configured to be a critical thinker. I bounce the idea, details, and constraints back and forth.</p><h3 id="3-the-sanity-check">3. The Sanity Check</h3><p>I explicitly ask the AI: &#x201C;Do you understand the idea and the requirements?&#x201D; It&#x2019;s a simple step, but it forces the model to synthesize the conversation</p><h3 id="4-the-reverse-interrogation">4. The Reverse Interrogation</h3><p>This is the most important step in the beginning. I ask the AI: &#x201C;What questions do you have? Do you see any logical gaps or edge cases?&#x201D; I don&#x2019;t proceed until the AI has poked holes in my logic.</p><h2 id="phase-2-the-specification-the-contract">Phase 2: The Specification (The Contract)</h2><p>Once we &quot;feel&quot; aligned, I don&apos;t ask it to code. I ask it to write.</p><h3 id="5-the-doc-writer-mode">5. The &quot;Doc Writer&quot; Mode</h3><p>I switch to a different custom persona focused solely on technical documentation.</p><h3 id="6-drafting-the-spec">6. Drafting the Spec</h3><p>I instruct the writer to create a feature specification based on our previous chat. I enforce a declarative style (describing what should happen, not just how). The spec must include:</p><p>* &#xA0; The current state (and the state after changes).<br>* &#xA0; Goals and desired outcomes.<br>* &#xA0; Security concerns.<br>* &#xA0; Relevant code locations</p><h3 id="7-fact-checking">7. Fact Checking</h3><p>I open a new context and ask the agent to fact check the assumptions made in the spec using web search and documentation (e.g. Context7)</p><h3 id="8-the-peer-review">8. The Peer Review</h3><p>I open a new context with a different &quot;smart&quot; model (often a different LLM entirely). I feed it the spec and ask it to review and improve it.</p><h3 id="9-human-review">9. Human Review</h3><p>I scan through the polished spec. If something feels off or unclear, I loop back to the AI to fix the text. I never move to code until the words are right.</p><h2 id="phase-3-the-implementation-and-the-break">Phase 3: The Implementation (and The Break)<br></h2><p>Now that we have a solid &quot;contract&quot; of what needs to be built, the coding is actually the easiest part.</p><h3 id="10-the-handoff">10. The Handoff</h3><p>I open a new context and give the spec to a smart coding agent. I tell it: &#x201C;Implement this specification.&#x201D;</p><h3 id="11-touch-grass">11. Touch Grass</h3><p>This is where the workflow pays off. Because the spec is detailed, I don&#x2019;t need to hover. I go read Hacker News, grab a coffee, or actually step outside. I let the machine do the heavy lifting.</p><h2 id="phase-4-the-loop-and-the-polish">Phase 4: The Loop and The Polish</h2><h3 id="12-the-qa-loop">12. The QA Loop:</h3><p>When I come back, I test the implementation. It&#x2019;s rarely perfect on the first shot. I enter a feedback loop, pointing out bugs or UI issues, and the agent iterates.</p><h3 id="13-the-ai-code-review">13. The AI Code Review</h3><p>Once it seems to work, I&#x2019;m still not done. I open a fresh context with another smart AI. I feed it the original specification and the current git changes (the `diff`). I ask it to review the code specifically against the spec, looking for:</p><p>* &#xA0; Logical consistency.<br>* &#xA0; Security vulnerabilities.<br>* &#xA0; Performance bottlenecks.<br>* &#xA0; Project structure consistency.</p><h3 id="14-refinement">14. Refinement</h3><p>I have the agent implement the recommended improvements from the review.</p><h3 id="15-final-human-review">15. Final Human Review</h3><p>I do one last pass of QA and code review myself.</p><h3 id="16-ship-it">16. Ship It</h3><p>When I&#x2019;m happy, I commit the code. Crucially, I also commit the specification file. It serves as documentation for the future. Then, I push and ship.</p><h2 id="why-this-works-for-me">Why this works for me</h2><p>Is 15 steps overkill? Maybe. I know plenty of developers who can move much faster than this.</p><p>However, I&#x2019;ve learned that I am prone to getting lost in the weeds if I don&apos;t have a clear plan. This workflow essentially forces me to slow down. By treating the AI as a partner that needs clear instructions rather than a magic wand, I find I spend less time untangling confused code later on.</p><p>It&#x2019;s definitely a bit process-heavy, and I&#x2019;m sure I&#x2019;ll tweak it again next month. But for now, this structure gives me the confidence to ship features without constantly worrying that I missed something obvious. Plus, the coffee breaks are a nice bonus.</p>]]></content:encoded></item><item><title><![CDATA[Working Effectively With AI Coding Agents]]></title><description><![CDATA[How do I develop and get value from AI coding agents? There is a lot of hype and unmet expectations but I think it's still enormously beneficial.]]></description><link>https://adnan-m.me/how-i-develop-with-ai/</link><guid isPermaLink="false">68988820db737a059ba2eb20</guid><category><![CDATA[software engineering]]></category><category><![CDATA[ai]]></category><category><![CDATA[llms]]></category><category><![CDATA[anthropic]]></category><category><![CDATA[google]]></category><category><![CDATA[openai]]></category><category><![CDATA[php]]></category><category><![CDATA[golang]]></category><category><![CDATA[go]]></category><category><![CDATA[architecture]]></category><category><![CDATA[productivity]]></category><category><![CDATA[advice]]></category><dc:creator><![CDATA[Adnan Mujkanovic]]></dc:creator><pubDate>Sun, 10 Aug 2025 14:41:26 GMT</pubDate><media:content url="https://adnan-m.me/content/images/2025/08/aniket-deole-M6XC789HLe8-unsplash-1-.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: html--><div id="contents"></div><!--kg-card-end: html--><h2 id="provide-as-much-context-as-possible">Provide as much context as possible</h2><img src="https://adnan-m.me/content/images/2025/08/aniket-deole-M6XC789HLe8-unsplash-1-.jpg" alt="Working Effectively With AI Coding Agents"><p>Describe what you want in clear, concrete detail. Include examples. Link to relevant files, standards, and conventions the agent should follow.</p><p>Before you let the agent write code, ask it to restate your requirements. <strong>Read</strong> the response. If it captured your intent, proceed. If not, clarify what it missed.</p><h2 id="tell-it-what-to-do%E2%80%94not-what-not-to-do">Tell it what to do&#x2014;not what <em>not</em> to do</h2><p>&#x201C;Don&#x2019;t do X&#x201D; rarely produces good results. Positive instructions are far more reliable. Specify the desired outcome, constraints, and acceptance criteria.</p><h2 id="get-the-architecture-right">Get the architecture right</h2><p>Tuning a function is easy; reworking an architecture is not. Invest in the design upfront and verify that the agent is implementing <strong>your</strong> architecture. How it writes small helpers matters less than whether the system boundaries and data flows are correct.</p><h2 id="ask-questions-to-gather-context">Ask questions to gather context</h2><p>If you&#x2019;re not yet sure what you want, use the agent as a thinking partner. Discuss the project, the stack, the docs, even Reddit threads. This both sharpens your own understanding and gives the agent richer context.</p><h2 id="ground-the-model-in-facts">Ground the model in facts</h2><p>Training cutoffs and hallucinations are real. LLMs aren&#x2019;t omniscient&#x2014;and they do make mistakes. Counteract this by grounding the agent with up-to-date, verifiable facts: documentation, specs, tickets, code comments, scientific papers, web search results, etc.</p><h2 id="keep-task-scope-tight">Keep task scope tight</h2><p>As prompts and context grow, signal can get diluted. Break work into small, well-bounded tasks so the context contains only what&#x2019;s relevant. Smaller tasks make it easier to review, test, and course-correct.</p><h2 id="refresh-the-context-regularly">Refresh the context regularly</h2><p>Think &#x201C;one task, one context.&#x201D; If the current thread gets noisy or off track, shelve the partial changes and start a fresh context with a narrower scope and cleaner prompt.</p><h2 id="sync-state-with-git-diff">Sync state with <code>git diff</code></h2><p>If you like the current code but the conversation has drifted, open a new context and have the agent run <code>git diff</code> (or paste the diff) to catch up without reintroducing the old noise.</p><h2 id="let-llms-review-each-other%E2%80%99s-code">Let LLMs review each other&#x2019;s code</h2><p>Create a &#x201C;review mode&#x201D; prompt describing what you care about: correctness, readability, performance, security, edge cases, etc. Run reviews with a second model&#x2014;or the same model in review mode&#x2014;and ask targeted questions like:</p><p>&#x201C;What risks or failure modes do you see?&#x201D;</p><p>&#x201C;Which assumptions should be documented?&#x201D;</p><p>&#x201C;What important details might be missing?&#x201D;</p><h2 id="use-tdd">Use TDD</h2><p>Once the architecture is solid, start with tests. Have the agent draft end-to-end or unit tests, review them yourself, then let the agent implement the feature and iterate against the tests. This keeps development anchored to real behavior.</p><h2 id="summary">Summary</h2><p>AI coding agents aren&#x2019;t replacing software engineers anytime soon&#x2014;and that&#x2019;s a good thing. They still need thoughtful direction, review, and course correction to produce great work.</p><p>What they <em>do</em> offer is speed on well-defined tasks. Offload the mechanical, boilerplate, and repetitive work so you can spend more time on design, problem-solving, and asking better questions. Used this way, LLMs and agents make development faster and often higher quality&#x2014;because you can iterate more, learn faster, and keep your focus on the hard parts.</p>]]></content:encoded></item><item><title><![CDATA[How I Structure My Software Projects]]></title><description><![CDATA[Times, requirements, technology changes constantly. If we are not able to catch up in a timely manner, we are putting the business under increasing risk.]]></description><link>https://adnan-m.me/how-i-structure-my-software-projects-2/</link><guid isPermaLink="false">67ea53d51e74e804f29b20d3</guid><category><![CDATA[architecture]]></category><category><![CDATA[domain-driven]]></category><category><![CDATA[design]]></category><category><![CDATA[golang]]></category><category><![CDATA[decoupling]]></category><category><![CDATA[cohesion]]></category><category><![CDATA[infrastructure]]></category><category><![CDATA[complexity]]></category><category><![CDATA[databases]]></category><category><![CDATA[DDD]]></category><category><![CDATA[engineering]]></category><dc:creator><![CDATA[Adnan Mujkanovic]]></dc:creator><pubDate>Mon, 31 Mar 2025 10:04:54 GMT</pubDate><media:content url="https://adnan-m.me/content/images/2025/03/keyur-nandaniya-7ZBWyxFWLQU-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: html--><div id="contents"></div><!--kg-card-end: html--><h2 id="what-do-i-want-to-achieve-with-the-architecture">What Do I Want To Achieve With The Architecture?</h2><h3 id="future-proofing-optionality">Future Proofing / Optionality</h3><img src="https://adnan-m.me/content/images/2025/03/keyur-nandaniya-7ZBWyxFWLQU-unsplash.jpg" alt="How I Structure My Software Projects"><p>Times, requirements, technology changes constantly. If we are not able to catch up in a timely manner, we are putting the business under increasing risk. That&apos;s why it&apos;s important to make an effort and keep as many options open as possible for the future this is assuming we want to be in business long-term.</p><h3 id="modularity">Modularity</h3><p>I want to be able to deal and manage ever increasing complexity in an effective way. Breaking the system down into smaller self-contained parts is crucial for this purpose.</p><h3 id="separate-concerns">Separate concerns</h3><p>I want to improve maintainability, scalability, and reusability by allowing me and others to focus on individual parts without affecting the entire system.</p><h3 id="independent-magic">Independent Magic</h3><p>When we write software, we usually want to solve a particular problem for our customers. This solution is what I call the &quot;Magic&quot;. We want to keep this magic part as independent as possible from secondary concerns.</p><h2 id="what-does-it-look-like">What Does It Look like?</h2><h3 id="the-three-parts">The Three Parts</h3><figure class="kg-card kg-image-card"><img src="https://adnan-m.me/content/images/2025/03/architecture-1-.png" class="kg-image" alt="How I Structure My Software Projects" loading="lazy" width="2000" height="828" srcset="https://adnan-m.me/content/images/size/w600/2025/03/architecture-1-.png 600w, https://adnan-m.me/content/images/size/w1000/2025/03/architecture-1-.png 1000w, https://adnan-m.me/content/images/size/w1600/2025/03/architecture-1-.png 1600w, https://adnan-m.me/content/images/size/w2400/2025/03/architecture-1-.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>From a birds-eye view, I like to separate the software architecture into three main parts: entry points, the magic and supporting infrastructure.</p><h3 id="entry-points">Entry Points</h3><p>This is the surface our users interact with, the application program interface (API) of our software.</p><h3 id="the-magic">The Magic</h3><p>This is where the magic happens. This is our primary concern where we deal with solving the particular problems of our domain of interest. Here we don&apos;t want to deal with http, database particularities, etc. This where we want to implement inversion of control.</p><h3 id="supporting-infrastructure">Supporting Infrastructure</h3><p>This is the roads, the vehicles, the buildings that we need to run our business. In the world of software these are called databases, caches, queues, object storage, etc.</p><h2 id="what-is-the-folder-structure">What Is The Folder Structure?</h2><p>This is roughly what the folder structure might look like:</p><p>Model<br>|_ User<br>|_ Product<br>|_ ...</p><p>UseCase<br>|_ Register<br>|_ Login<br>|_ AddToCart<br>|_ ...</p><p>EntryPoint<br>|_ Http<br>| &#xA0; |_ Register<br>| &#xA0; |_ Login<br>| &#xA0; |_ AddToCart<br>| &#xA0; |_ ...<br>|_ Queue<br> &#xA0; &#xA0;|_ SendActivationEmail<br> &#xA0; &#xA0;|_ SendOrderSummary<br> &#xA0; &#xA0;|_ ...</p><p>Container<br>|_ Database<br>|_ Queue<br>|_ Cache</p><h2 id="summary">Summary</h2><p>We call it domain-driven design, hexagonal architecture and so on which is great. Bottom line is, we want to support the business i.e. customers long-term and be able to respond to change in the most effective and nimble way possible. To achieve this we have to think about optionality, modularity, separations of concerns, cohesion, coupling and so on when designing the architecture of our systems.</p>]]></content:encoded></item><item><title><![CDATA[Bounded Context in Domain-Driven Design]]></title><description><![CDATA[What are bounded contexts about? As usual, they're about splitting responsibilities to make complex systems simpler and easier manage]]></description><link>https://adnan-m.me/bounded-context-in-domain-driven-design/</link><guid isPermaLink="false">67d7c1371e74e804f29b20b6</guid><category><![CDATA[complexity]]></category><category><![CDATA[architecture]]></category><category><![CDATA[DDD]]></category><category><![CDATA[domain-driven]]></category><category><![CDATA[design]]></category><category><![CDATA[software]]></category><category><![CDATA[engineering]]></category><dc:creator><![CDATA[Adnan Mujkanovic]]></dc:creator><pubDate>Mon, 17 Mar 2025 06:31:42 GMT</pubDate><media:content url="https://adnan-m.me/content/images/2025/03/jeremy-thomas-O6N9RV2rzX8-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://adnan-m.me/content/images/2025/03/jeremy-thomas-O6N9RV2rzX8-unsplash.jpg" alt="Bounded Context in Domain-Driven Design"><p>The age old question, what is a &quot;Bounded Context&quot; in Domain-Driven Design?</p><p>- A bounded context is where terms have clear and unambiguous meaning and rules associated with it.</p><p>Where do you draw the border of a bounded context?</p><p>- The moment a term starts to have more than meaning.</p><h2 id="example-termorder">Example Term - Order</h2><p>Imagine you have a magic word &quot;Order&quot; that means different things in different playrooms of your big online store.</p><h3 id="1-sales-context-the-shopping-room">1. Sales Context (The Shopping Room)</h3><ul><li><strong>What it means:</strong> In this room, &quot;Order&quot; is when a customer picks their favorite toy, puts it in their cart, and pays for it.</li><li><strong>Why it&apos;s special:</strong> Here, &quot;Order&quot; captures everything about buying something&#x2014;like which toy was chosen, the price, and the customer&#x2019;s details.</li></ul><h3 id="2-fulfillment-context-the-shipping-room">2. Fulfillment Context (The Shipping Room)</h3><ul><li><strong>What it means:</strong> In another room, &quot;Order&quot; becomes a list that helps workers pack the toy, choose a box, and send it out the door.</li><li><strong>Why it&apos;s special:</strong> This room cares more about how to get the toy from the store to the customer&apos;s home, including things like shipping addresses and delivery dates.</li></ul><h3 id="3-customer-service-context-the-help-room">3. Customer Service Context (The Help Room)</h3><ul><li><strong>What it means:</strong> In this room, &quot;Order&quot; helps when someone needs a little extra help&#x2014;like fixing a mistake in the address or returning a toy.</li><li><strong>Why it&apos;s special:</strong> The details here are used to help the customer and answer questions, rather than to sell or ship the toy.</li></ul><h3 id="4-billingpayment-context-the-money-room">4. Billing/Payment Context (The Money Room)</h3><ul><li><strong>What it means:</strong> In another playroom, &quot;Order&quot; is more about checking that the money paid is right and that all the receipts and invoices are in order.</li><li><strong>Why it&apos;s special:</strong> This context focuses on making sure every cent is accounted for and the financial records are correct.</li></ul><h2 id="how-do-we-decide-where-to-draw-the-boundaries">How Do We Decide Where to Draw the Boundaries?</h2><p>We create these separate rooms&#x2014;or <strong>bounded contexts</strong>&#x2014;when we notice that the same word &quot;Order&quot; has different rules and details in different parts of our business. By drawing these boundaries, each team (like Sales, Shipping, Customer Service, and Billing) can work in their own room without mixing up their special rules.</p><p>Each room uses &quot;Order&quot; in a way that makes sense for that part of the business. This keeps everything neat and helps everyone understand exactly what they need to do, just like keeping different LEGO sets in separate boxes makes it easier to build a spaceship and a castle without getting the pieces mixed up.</p><p>As usual, it&apos;s about splitting responsibilities to make complex systems simpler and easier manage</p>]]></content:encoded></item><item><title><![CDATA[The Myth of Best Practices in Software Architecture: A Pragmatic Approach]]></title><description><![CDATA[The phrase "best practices" is often thrown around in software engineering, serving as a reassuring beacon of correctness. However, in the realm of software architecture, its application is far more nuanced.]]></description><link>https://adnan-m.me/the-myth-of-best-practices-in-software-architecture-a-pragmatic-approach/</link><guid isPermaLink="false">67cac32c17051cde84b77b6f</guid><category><![CDATA[architecture]]></category><category><![CDATA[software development]]></category><category><![CDATA[software engineering]]></category><category><![CDATA[complexity]]></category><category><![CDATA[consistency]]></category><category><![CDATA[databases]]></category><category><![CDATA[distributed systems]]></category><category><![CDATA[scalability]]></category><dc:creator><![CDATA[Adnan Mujkanovic]]></dc:creator><pubDate>Fri, 07 Mar 2025 10:01:15 GMT</pubDate><media:content url="https://adnan-m.me/content/images/2025/03/alan-tang-ZriVXRZbFqk-unsplash-1-.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: html--><div id="contents"></div><!--kg-card-end: html--><h2 id="introduction">Introduction</h2><img src="https://adnan-m.me/content/images/2025/03/alan-tang-ZriVXRZbFqk-unsplash-1-.jpg" alt="The Myth of Best Practices in Software Architecture: A Pragmatic Approach"><p>The phrase &quot;best practices&quot; is often thrown around in software engineering, serving as a reassuring beacon of correctness. However, in the realm of software architecture, its application is far more nuanced. Unlike development tasks where established patterns often apply universally, architecture is inherently contextual, requiring careful trade-off analysis rather than rigid adherence to predefined &quot;best&quot; solutions. In this article, we&#x2019;ll explore why architecture demands a different approach, the importance of evaluating trade-offs, and how data management plays a crucial role in shaping long-term decisions.</p><h2 id="the-unique-challenges-of-software-architecture">The Unique Challenges of Software Architecture</h2><p>Software development, at a small scale, is filled with well-documented solutions. Need to parse a JSON file? There are libraries for that. Implementing authentication? Frameworks provide well-established patterns. However, architecture is a dimension where solutions are not so easily transplanted. Each decision has broad implications that extend beyond code implementation, impacting scalability, maintainability, and business agility.</p><p>Unlike coding where we can find answers to small scale challenges relatively easily &#xA0;via LLMs or documentation, architecture is a problems space rarely identical across organizations. The structure of teams, business priorities, regulatory concerns, and existing technology stacks all shape architectural decisions. As a result, architecture cannot rely on generic &quot;best practices&quot;&#x2014;it must be tailored to the specific needs and constraints of a given environment.</p><h2 id="trade-offs-over-best-solutions">Trade-offs Over &quot;Best&quot; Solutions</h2><p>Rather than searching for the &quot;best&quot; architecture, effective architects focus on identifying the <em>least problematic</em> design given a set of constraints. Every architectural decision involves trade-offs, and a successful architecture is one that makes informed compromises rather than blindly following industry trends.</p><p>For example, microservices are often touted as a best practice for scalability and flexibility. However, adopting microservices introduces significant complexity in areas like service discovery, observability, and distributed data consistency. In many cases, a monolithic architecture may be the better choice for reducing operational overhead and accelerating development speed. The key is to understand the trade-offs and make decisions that align with the organization&#x2019;s current and future needs.</p><p>Architects must constantly ask:</p><ul><li>What are the long-term maintenance implications of this choice?</li><li>How does this decision impact system performance and reliability?</li><li>Are we optimizing for the right constraints, or just following industry trends?</li></ul><h2 id="the-fluid-nature-of-architecture">The Fluid Nature of Architecture</h2><p>Technology is constantly evolving, and architectural paradigms shift accordingly. A decade ago, Service-Oriented Architecture (SOA) was the gold standard for enterprise systems. Today, many organizations have embraced microservices, and some are now reconsidering them in favor of monolithic or modular monolith approaches due to the challenges of distributed systems.</p><p>Architects must recognize that today&#x2019;s &quot;best practice&quot; may become tomorrow&#x2019;s legacy burden. Rather than dogmatically adhering to trends, successful architects embrace change and design systems that can evolve over time. This means:</p><ul><li>Avoiding premature optimization for scale if the business doesn&#x2019;t require it.</li><li>Prioritizing modularity and well-defined boundaries over rigid adherence to architectural dogma.</li><li>Designing for adaptability, ensuring that future changes can be made without excessive refactoring.</li></ul><h2 id="the-overlooked-importance-of-data-architecture">The Overlooked Importance of Data Architecture</h2><p>One of the most critical yet often overlooked aspects of software architecture is data. Unlike application logic, which can be refactored relatively easily, data models and storage decisions are far more challenging to change once they are in production.</p><p>Effective architects understand that:</p><ul><li><strong>Data consistency and integrity</strong> are foundational to system reliability.</li><li><strong>Choosing the right database model</strong> (relational vs. NoSQL, event sourcing, etc.) has long-term implications.</li><li><strong>Data governance and security</strong> must be factored into architectural decisions early on.</li><li><strong>Scalability and query patterns</strong> should be considered when designing schemas, ensuring that the chosen approach aligns with expected data access patterns.</li></ul><p>Ignoring data architecture in favor of purely focusing on service design can lead to significant challenges down the road, including performance bottlenecks, data fragmentation, and costly migrations.</p><h2 id="conclusion">Conclusion</h2><p>Software architecture is not about applying &quot;best practices&quot; but about making informed decisions based on trade-offs, organizational context, and long-term sustainability. The most effective architects understand that no two problems are identical and that flexibility is crucial in an ever-evolving technological landscape. By prioritizing trade-off analysis and paying close attention to data architecture, architects can build systems that are not only functional today but adaptable for the future.</p>]]></content:encoded></item><item><title><![CDATA[How To Manage Your Time?]]></title><description><![CDATA[There is a conflict here, between the things that need to be done and our time. Our job is to find a way to reconcile this conflict.]]></description><link>https://adnan-m.me/how-to-manage-your-time/</link><guid isPermaLink="false">67c872db17051cde84b77b57</guid><category><![CDATA[management]]></category><category><![CDATA[productivity]]></category><category><![CDATA[development]]></category><dc:creator><![CDATA[Adnan Mujkanovic]]></dc:creator><pubDate>Wed, 05 Mar 2025 15:53:01 GMT</pubDate><media:content url="https://adnan-m.me/content/images/2025/03/todd-trapani-aVVAI7z0WoY-unsplash-1-.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://adnan-m.me/content/images/2025/03/todd-trapani-aVVAI7z0WoY-unsplash-1-.jpg" alt="How To Manage Your Time?"><p>To make it short, just forget about it. Work is too dynamic to be able to predict a schedule and consistently live up to it. You will fail and you will feel bad about it.</p><p>What to do instead?</p><p>Focus on what needs to be done. Duh. :D</p><p>So what you need to do is to keep a list of things, regularly update the list and if you are lagging behind in getting things done, put more effort in to catch up with all the tasks. I&apos;m kidding, DON&apos;T do this. It&apos;s a trap.</p><p>If you are like most people, then you know, that things to do are always abundant contrary to our time, which there is never enough of.</p><p>There is a <strong>conflict</strong> here, between the things that need to be done and our time. Our job is to find a way to reconcile this conflict.</p><p>Now let&apos;s share the <strong>secret</strong> here.</p><p>1. You can&apos;t and you don&apos;t need to do all things.</p><p>2. Counter to <strong>intuition</strong>, you need to focus most of your time on things with the highest ROI instead of the things you think are &quot;time sensitive&quot;</p><h2 id="the-eisenhower-matrix">The Eisenhower Matrix</h2><p>&quot;I have two kinds of problems, the urgent and the important. The urgent are not important and the important are never urgent&quot; - Eisenhower</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://adnan-m.me/content/images/2025/03/Untitled-2024-05-27-0811-3-.png" class="kg-image" alt="How To Manage Your Time?" loading="lazy" width="2000" height="1547" srcset="https://adnan-m.me/content/images/size/w600/2025/03/Untitled-2024-05-27-0811-3-.png 600w, https://adnan-m.me/content/images/size/w1000/2025/03/Untitled-2024-05-27-0811-3-.png 1000w, https://adnan-m.me/content/images/size/w1600/2025/03/Untitled-2024-05-27-0811-3-.png 1600w, https://adnan-m.me/content/images/2025/03/Untitled-2024-05-27-0811-3-.png 2169w" sizes="(min-width: 720px) 720px"><figcaption>Yet another twist on the &quot;Eisenhower Matrix&quot;</figcaption></figure><p>Let tasks go through the pipeline:</p><ol><li>This important? No! This urgent? No! - Throw it away!</li><li>This important? No! This urgent? Yes! - Delegate!</li><li>This important? Yes! This urgent? No! - Work on this most of the time for the highest impact and most reward!</li><li>This important? Yes! This urgent? Yes! - Briefly pause #3 for this and do it now. Find ways of avoiding similar instances of this in the future.</li></ol><p>You might not get the hang of it immediately but internalizing and optimizing this over time, will give you an immense progress boost in the long run.</p>]]></content:encoded></item><item><title><![CDATA[Software Development Bandwidth]]></title><description><![CDATA[The goal is to deliver value to customers as fast as possible while staying in line with business goals and keeping quality at a high level. As we know from experience, fast and high quality usually don't work well together. Let's see what we can do about it.]]></description><link>https://adnan-m.me/software-development-bandwidth/</link><guid isPermaLink="false">666eab8717051cde84b7786c</guid><category><![CDATA[agile]]></category><category><![CDATA[code quality]]></category><category><![CDATA[complexity]]></category><category><![CDATA[project management]]></category><category><![CDATA[developer experience]]></category><category><![CDATA[tdd]]></category><dc:creator><![CDATA[Adnan Mujkanovic]]></dc:creator><pubDate>Sun, 16 Jun 2024 17:21:22 GMT</pubDate><media:content url="https://adnan-m.me/content/images/2024/06/software-development-bandwidth.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: html--><div id="contents"></div><!--kg-card-end: html--><h2 id="intro">Intro</h2><img src="https://adnan-m.me/content/images/2024/06/software-development-bandwidth.jpg" alt="Software Development Bandwidth"><p>Software development is a non-trivial endeavour. The goal is to deliver value to customers as fast as possible while staying in line with business goals and keeping quality at a high level. As we know from experience, fast and high quality usually don&apos;t work well together. Let&apos;s see what we can do about it.</p><h2 id="what-is-lead-time">What Is Lead Time?</h2><p>Lead time in software development refers to the total time elapsed from when a requirement or feature is initially identified until it is successfully delivered and deployed into production for end users.</p><p>Lead time encompasses the entire value stream, including time spent in backlogs or waiting for resources, coding, testing, approvals, and deployment.</p><p>Shorter lead times indicate higher agility and responsiveness to customer needs and market changes.</p><p>Long lead times can signify bottlenecks, inefficiencies, or constraints in the development workflow that need to be addressed.</p><p>Tracking and optimizing lead time is crucial for software teams as it directly impacts their ability to rapidly deliver value to customers and stay competitive. Reducing lead time is a key goal of DevOps and agile methodologies.</p><h2 id="how-to-optimize-lead-time">How To Optimize Lead Time?</h2><p>Since lead time involves many steps and stages, we first have to look at all these steps and try to identify where to start. To optimize lead time we can use the <a href="https://www.adnan-m.me/factors-influencing-organizational-success/#theory-of-constraints">&quot;theory of constraints&quot;</a> to help us identify bottlenecks.</p><h2 id="what-is-the-theory-of-constraints">What Is The &quot;Theory of Constraints&quot;?</h2><p>The theory of constraints (TOC) is a management philosophy and methodology developed by Eliyahu M. Goldratt. It is based on the principle that every system has at least one constraint that limits its performance relative to its goal.</p><p>The throughput of the entire system is limited by its constraint. Improving non-constraints will not increase throughput.</p><h2 id="what-is-the-constraint">What Is The Constraint?</h2><p>Lead time in software development can have many constraints like:</p><ul><li>Policy constraints like unnecessary approval gates or handoffs between siloed teams (dev, testing, ops)</li><li>Lack of team expertise or having too small/large a team size</li><li>Unclear requirements or frequent scope changes leading to scope creep</li><li>Resource constraints like insufficient hardware, tools or development environments</li><li>Dependencies on external services, APIs or components that cause integration delays</li><li>Poor communication and collaboration within the team or with stakeholders</li></ul><p>As usual, every organization is unique and should analyze their specific context to understand what the constraint is they should focus on.</p><p>But in this article I will focus on one particular constraint - batch size.</p><h2 id="what-is-batch-size">What Is Batch Size?</h2><p>The batch size in software development is the amount of code changes going through and waiting in the pipeline (review, approval, acceptance, QA) before being deployed into production.</p><h2 id="what-is-bandwidth">What Is Bandwidth?</h2><p>Bandwidth is a measure of the maximum data transfer capacity of a network or internet connection. It refers to the amount of data that can be transmitted over a network in a given time period.</p><p>If you try to send more data than the available bandwidth can handle, it will result in packet loss and degraded performance.</p><h2 id="the-analogy">The Analogy</h2><p>The simple analogy I will use here is that the software development value stream has a certain amount of bandwidth.</p><p>If you try to push more through the stream than the available bandwidth can handle, it will result in slow and degraded performance, less agility, more bugs, etc.</p><h2 id="less-is-more">Less Is More</h2><p>This is one of those problems where the solution is counter-intuitive. This means that if we want to be more agile, deliver value to the customer faster without compromising on quality, we have to deliver less at a time.</p><p>By delivering less at a time we gain the following benefits:</p><ul><li>Easier writing of automated tests</li><li>Increased automated test coverage</li><li>Faster code reviews</li><li>Easier catching of issues during reviews</li><li>Higher code quality</li><li>Faster feedback cycles</li><li>Faster response to information</li><li>Less technical debt</li><li>Lower cognitive load</li><li>Increased developer satisfaction</li><li>Actual continues integration</li><li>Easier refactorings</li><li>and more ...</li></ul><h2 id="how-to-implement-smaller-batch-sizes">How To Implement Smaller Batch Sizes?</h2><p>Unfortunately, we cannot implement this over night and with only good will. It requires change in mindset and some new ground rules i.e. constraints.</p><p>Some recommendations are:</p><ul><li>Start small on a fresh smaller size project</li><li>Do smaller projects in general</li><li>Split user stories and tasks</li><li>Pair Programming</li><li>Write automated tests (easier since changes much smaller)</li><li>Use red-green-refactor cycle</li><li>Use the &quot;branch by abstraction&quot; pattern</li><li>Do synchronous code reviews</li><li>Deploy changes every day or morning as a rule</li><li>Decouple deployment from release through feature flags</li><li>Use trunk-based development</li></ul><h2 id="conclusion">Conclusion</h2><p>Sometimes less is more and this is the case for many software development teams. To deliver value to the customer faster and with a higher quality, we need to understand that the value stream pipeline has a limited bandwidth and we need to act accordingly. Maybe this is not the solution for your team, but maybe it&apos;s worth a try.</p>]]></content:encoded></item><item><title><![CDATA[Terraform - Automated Testing, Security & Code Quality]]></title><description><![CDATA[You used testing frameworks in your favorite language before and now you want to know how to improve code quality and write tests for your Terraform IaC. In that case, continue reading.]]></description><link>https://adnan-m.me/terraform-automated-testing-security-code-quality/</link><guid isPermaLink="false">664f253ecc0e3f052bc4e979</guid><category><![CDATA[testing]]></category><category><![CDATA[code quality]]></category><category><![CDATA[terraform]]></category><category><![CDATA[CI/CD]]></category><category><![CDATA[cloud]]></category><category><![CDATA[security]]></category><dc:creator><![CDATA[Adnan Mujkanovic]]></dc:creator><pubDate>Sat, 01 Jun 2024 12:58:09 GMT</pubDate><media:content url="https://adnan-m.me/content/images/2024/06/terraform-iac-testing-and-qa.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: html--><div id="contents"></div><!--kg-card-end: html--><h2 id="intro">Intro</h2><img src="https://adnan-m.me/content/images/2024/06/terraform-iac-testing-and-qa.jpg" alt="Terraform - Automated Testing, Security &amp; Code Quality"><p>If you did any kind of software development, you hopefully also wrote some automated tests before. Unit tests are amazing and blazing fast (or at least they should be). Integration or E2E tests are a bit more difficult because you might need to prepare the appropriate environment and components the tests depend on. But still, setting up that environment and components once should last you a long time until you might need another component in the future. If you ran some plans and applies with Terraform in the past, you know that they don&apos;t always work as expected. Question is how do we increase the reliability of Terraform tasks in advance with automated tests?</p><h2 id="built-in-terraform-testing-and-qa">Built-In Terraform Testing And QA</h2><p>Terraform provides built-in testing features so let&apos;s look at those first.</p><h3 id="terraform-fmt">Terraform fmt</h3><p>We all know that consistent formatting tremendously improves readability. For this purpose we can and should use the <a href="https://developer.hashicorp.com/terraform/cli/commands/fmt">terraform fmt command</a>.</p><!--kg-card-begin: markdown--><pre><code>terraform fmt
</code></pre>
<!--kg-card-end: markdown--><h3 id="terraform-validate-command">Terraform Validate Command</h3><p>One of the simplest things we can do is running the Terraform validate command which will validate that our syntax is valid and that our code is internally consistent.</p><!--kg-card-begin: markdown--><pre><code>terraform validate
</code></pre>
<!--kg-card-end: markdown--><p>Terraform validate is the least QA you should add to your CI.</p><h3 id="input-validation">Input Validation</h3><p>Next thing we can do is validate that our inputs meet desired conditions.</p><!--kg-card-begin: markdown--><pre><code>variable &quot;aws_private_subnet_ids&quot; {  
  description = &quot;VPC private subnet ids.&quot;  
  type        = list(string)  

  validation {    
    condition     = length(var.aws_private_subnet_ids) &gt; 1    
    error_message = &quot;This application requires at least two private subnets.&quot;  
  }
}
</code></pre>
<!--kg-card-end: markdown--><p>This will help you validate inputs upon running Terraform plan.</p><h3 id="preconditions">Preconditions</h3><p>Introduced in Terraform 1.2, <a href="https://developer.hashicorp.com/terraform/language/expressions/custom-conditions#preconditions-and-postconditions">preconditions</a>, as the name suggests, can help you validate that desired conditions are met when running Terraform plan.</p><!--kg-card-begin: markdown--><pre><code>data &quot;aws_ec2_instance_type&quot; &quot;app&quot; {
  instance_type = var.aws_instance_type
}

resource &quot;aws_instance&quot; &quot;app&quot; {
  count = var.aws_instance_count

  instance_type = var.aws_instance_type
  ami           = var.aws_ami_id

  subnet_id              = var.aws_private_subnet_ids[count.index % length(var.aws_private_subnet_ids)]
  vpc_security_group_ids = [module.app_security_group.security_group_id]

  lifecycle {
    precondition {
      condition     = var.aws_instance_count % length(var.aws_private_subnet_ids) == 0
      error_message = &quot;The number of instances (${var.aws_instance_count}) must be evenly divisible by the number of private subnets (${length(var.aws_private_subnet_ids)}).&quot;
    }

    precondition {
      condition     = data.aws_ec2_instance_type.app.ebs_optimized_support != &quot;unsupported&quot;
      error_message = &quot;The EC2 instance type (${var.aws_instance_type}) must support EBS optimization.&quot;      
    }
  }
}
</code></pre>
<!--kg-card-end: markdown--><h3 id="postconditions">Postconditions</h3><p>Use postconditions to validate that desired conditions are met after we run apply.</p><!--kg-card-begin: markdown--><pre><code>data &quot;aws_vpc&quot; &quot;app&quot; {
  id = var.aws_vpc_id

  lifecycle {
    postcondition {
      condition     = self.enable_dns_support == true
      error_message = &quot;The selected VPC must have DNS support enabled.&quot;      
    }
  }
}

</code></pre>
<!--kg-card-end: markdown--><h3 id="prepostconditions">Pre/Postconditions</h3><p>Pre and postconditions are great for intercepting errors that would be thrown with generic error messages that might not be so helpful to users. Instead you can use pre/postconditions to intercept those errors and provide more useful, context specific error messages.</p><h3 id="checks-and-assertions">Checks And Assertions</h3><p>Available from Terraform version 1.5, we have checks and assertions at our disposal.</p><!--kg-card-begin: markdown--><pre><code>check &quot;health_check&quot; {
  data &quot;http&quot; &quot;terraform_io&quot; {
    url = &quot;https://www.example.com/some/api&quot;
  }

  assert {
    condition = data.http.terraform_io.status_code == 200
    error_message = &quot;${data.http.terraform_io.url} returned an unhealthy status code&quot;
  }
}

</code></pre>
<!--kg-card-end: markdown--><p>Use checks to perform custom checks that will be executed at the end of plan and apply and warn you through error messages of failed checks.</p><h3 id="the-terraform-testing-framework">The Terraform Testing Framework</h3><p>NOTE: <br>This one is available from Terrafrom version 1.6 which with the new BSL licence instead of the previous MPL 2.0 licence. You should read about it online and see if and how it matters to you.</p><p>The Terraform test framework was introduced in version 1.6. It allows you to run tests on real, short lived, resources. Running the tests on real resources is equivalent to <strong>integration testing (command = apply)</strong> which is the default behavior. You can also run the tests without actual resources as <strong>unit tests (command = plan)</strong>.</p><!--kg-card-begin: markdown--><pre><code># main.tf

provider &quot;aws&quot; {
    region = &quot;eu-central-1&quot;
}

variable &quot;bucket_prefix&quot; {
  type = string
}

resource &quot;aws_s3_bucket&quot; &quot;bucket&quot; {
  bucket = &quot;${var.bucket_prefix}-bucket&quot;
}

output &quot;bucket_name&quot; {
  value = aws_s3_bucket.bucket.bucket
}
</code></pre>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><pre><code># valid_string_concat.tftest.hcl

variables {
  bucket_prefix = &quot;test&quot;
}

run &quot;valid_string_concat&quot; {

  command = plan

  assert {
    condition     = aws_s3_bucket.bucket.bucket == &quot;test-bucket&quot;
    error_message = &quot;S3 bucket name did not match expected&quot;
  }

}

</code></pre>
<!--kg-card-end: markdown--><h2 id="third-party-testing-and-qa">Third-Party Testing and QA</h2><p>After exploring the built-in testing and QA capabilities of Terraform, let&apos;s look at some third-party tools.</p><h3 id="terratest">Terratest</h3><p>Terratest is GoLang library that enables us to write tests for our Terraform IaC in the form of Go tests (_test.go). This means you can enjoy a high-level language and all its features with built-in Terraform capabilities like init, plan, apply, reading outputs, etc.</p><!--kg-card-begin: markdown--><pre><code>package test

import (
	&quot;testing&quot;

	&quot;github.com/gruntwork-io/terratest/modules/terraform&quot;
	&quot;github.com/stretchr/testify/assert&quot;
)

func TestTerraformHelloWorldExample(t *testing.T) {
	// Construct the terraform options with default retryable errors to handle the most common
	// retryable errors in terraform testing.
	terraformOptions := terraform.WithDefaultRetryableErrors(t, &amp;terraform.Options{
		// Set the path to the Terraform code that will be tested.
		TerraformDir: &quot;../examples/terraform-hello-world-example&quot;,
	})

	// Clean up resources with &quot;terraform destroy&quot; at the end of the test.
	defer terraform.Destroy(t, terraformOptions)

	// Run &quot;terraform init&quot; and &quot;terraform apply&quot;. Fail the test if there are any errors.
	terraform.InitAndApply(t, terraformOptions)

	// Run `terraform output` to get the values of output variables and check they have the expected values.
	output := terraform.Output(t, terraformOptions, &quot;hello_world&quot;)
	assert.Equal(t, &quot;Hello, World!&quot;, output)
}
</code></pre>
<!--kg-card-end: markdown--><p>In addition to Terraform, Terratest also enables you to write tests for Dockerfiles, Kubernetes and a few other.</p><h3 id="tflint">TFLint</h3><p>TFLint is a Terraform linter that focuses on possible errors, best practices, and style conventions in Terraform code. It enforces coding standards, naming conventions, and checks for deprecated syntax or unused declarations.</p><!--kg-card-begin: markdown--><pre><code>docker run --rm -v /my/terraform/code:/data -t ghcr.io/terraform-linters/tflint
</code></pre>
<!--kg-card-end: markdown--><h3 id="trivy-tfsec">Trivy (TFSec)</h3><p>Trivy is a comprehensive and versatile security scanner. Trivy has <em>scanners</em> that look for security issues, and <em>targets</em> where it can find those issues.</p><p>TFSec will continue to remain available for the time being, although our engineering attention will be directed at Trivy going forward.</p><!--kg-card-begin: markdown--><pre><code>docker run -v /my/terraform/code:/data aquasec/trivy config /data
</code></pre>
<!--kg-card-end: markdown--><h3 id="terrascan">Terrascan</h3><p>Terrascan is a static code analyzer for Infrastructure as Code. Terrascan allows you to seamlessly scan infrastructure as code for misconfigurations, monitor provisioned cloud infrastructure for configuration changes that introduce posture drift, and enables reverting to a secure posture.</p><!--kg-card-begin: markdown--><pre><code>docker run -v /my/terraform/code:/data accurics/terrascan scan /data
</code></pre>
<!--kg-card-end: markdown--><h3 id="chekov">Chekov</h3><p>Checkov is a static code analysis tool focused on detecting security misconfigurations and compliance violations in Terraform code. It scans Terraform configurations and evaluates them against a comprehensive set of predefined security and compliance policies.</p><!--kg-card-begin: markdown--><pre><code>docker run --rm -v /my/terraform/code:/data bridgecrew/checkov -d /data
</code></pre>
<!--kg-card-end: markdown--><h2 id="conclusion">Conclusion</h2><p>You want to increase the code quality, security and compliance of your Terraform IaC? You want to make your IaC deployments more reliable? Great tools are available and, if not already the case, you should be integrating the tools and practices into your Terraform IaC game.</p><h2 id="references">References</h2><ul><li><a href="https://developer.hashicorp.com/terraform/language/expressions/custom-conditions">https://developer.hashicorp.com/terraform/language/expressions/custom-conditions</a></li><li><a href="https://developer.hashicorp.com/terraform/cli/commands/validate">https://developer.hashicorp.com/terraform/cli/commands/validate</a></li><li><a href="https://developer.hashicorp.com/terraform/language/tests">https://developer.hashicorp.com/terraform/language/tests</a></li><li><a href="https://terratest.gruntwork.io/docs/getting-started/quick-start/">https://terratest.gruntwork.io/docs/getting-started/quick-start/</a></li><li><a href="https://github.com/terraform-linters/tflint">https://github.com/terraform-linters/tflint</a></li><li><a href="https://github.com/aquasecurity/trivy">https://github.com/aquasecurity/trivy</a></li><li><a href="https://github.com/aquasecurity/tfsec">https://github.com/aquasecurity/tfsec</a></li><li><a href="https://hub.docker.com/r/tenable/terrascan">https://hub.docker.com/r/tenable/terrascan</a></li><li><a href="https://github.com/bridgecrewio/checkov">https://github.com/bridgecrewio/checkov</a></li></ul>]]></content:encoded></item><item><title><![CDATA[What Is Platform Engineering?]]></title><description><![CDATA[We hear these terms flying around, DevOps, SRE, Platform, etc. But what are all of these concepts about? How did they come to be? How can we use it?]]></description><link>https://adnan-m.me/what-is-platform-engineering/</link><guid isPermaLink="false">6630d84fce1dbddfec6c1bfb</guid><category><![CDATA[teams]]></category><category><![CDATA[agile]]></category><category><![CDATA[architecture]]></category><category><![CDATA[complexity]]></category><category><![CDATA[DevOps]]></category><category><![CDATA[developer experience]]></category><category><![CDATA[software engineering]]></category><dc:creator><![CDATA[Adnan Mujkanovic]]></dc:creator><pubDate>Wed, 01 May 2024 07:53:08 GMT</pubDate><media:content url="https://adnan-m.me/content/images/2024/05/what-is-platform-engineering-2.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: html--><div id="contents"></div><!--kg-card-end: html--><h2 id="intro">Intro</h2><img src="https://adnan-m.me/content/images/2024/05/what-is-platform-engineering-2.png" alt="What Is Platform Engineering?"><p>We hear these terms flying around, DevOps, SRE, Platform, etc. But what is it all about? How did they come to be? How can we use them?</p><h3 id="note">Note</h3><p>You already know this but the topics below are relevant only after software teams reach a certain scale. If it&apos;s only one or two of you starting from scratch, don&apos;t waste time on these topics and use the fastest and most pragmatic way to get your product out of the door.</p><h2 id="history">History</h2><h3 id="development-operations">Development &amp; Operations</h3><figure class="kg-card kg-image-card"><img src="https://adnan-m.me/content/images/2024/04/dev-ops-separated-1.png" class="kg-image" alt="What Is Platform Engineering?" loading="lazy" width="582" height="206"></figure><p>Once upon a time in a galaxy far, far away, there was a civilization divided into two classes - development and operations. They were supposed to work on a common goal but somewhere along the way they got isolated from each other and tensions grew. Operations did not know how to troubleshoot issues with the instructions given by development and development was not aware of the challenges operations faced. Eventually, progress came to a halt and this civilization collapsed. </p><h3 id="devops">DevOps</h3><figure class="kg-card kg-image-card"><img src="https://adnan-m.me/content/images/2024/04/dev-ops-together.png" class="kg-image" alt="What Is Platform Engineering?" loading="lazy" width="581" height="206"></figure><p>Learning from the problems previous civilizations faced, a new civilization emerged - DevOps. They understood that if they want to be successful, the different trades will need to work together and cover the whole process, from idea to delivering the value to society. Instead of development and operations being separated, the DevOps civilization was structured into smaller independent groups with members from both trades, development and operations. This civilization was quite successful and continues to exist.</p><h3 id="site-reliability-engineering">Site Reliability Engineering</h3><figure class="kg-card kg-image-card"><img src="https://adnan-m.me/content/images/2024/04/dev-ops-sre.png" class="kg-image" alt="What Is Platform Engineering?" loading="lazy" width="581" height="206"></figure><p>After some time of development and operations working closely together, a new trade emerged - the site reliability engineer. This started when someone asked a simple question - &quot;What if we treat operations as a development i.e. a software problem?&quot;. Soon we had members of society that were good at both, development and operations. These members were tasked with taking care of the critical parts of the infrastructure and services since they had good command and understanding of all the parts.</p><h3 id="platform-engineering">Platform Engineering</h3><figure class="kg-card kg-image-card"><img src="https://adnan-m.me/content/images/2024/04/platform-dev-ops-sre-1.png" class="kg-image" alt="What Is Platform Engineering?" loading="lazy" width="581" height="206"></figure><p>If you are a software engineer, you know that we love to build abstractions. Well, after operations and development were merged together into one, we started seeing abstractions being created everywhere. Over time, the good abstractions crystalized at the top. The DevOps and SRE teams started using those abstractions so they could move faster instead of reinventing the wheel over and over again. A new trade emerged - the platform engineer. Software engineers building products for other software engineers.</p><h2 id="what-is-a-platform">What Is A Platform?</h2><p>So platform engineering teams build products for software engineering teams but what are those products? What products make up the platform? Well, you have to ask your customers these questions. But what it&apos;s usually about is the following:</p><ul><li>a place to run applications, </li><li>a way to deploy applications,</li><li>a way to deploy supporting infrastructure</li><li>a way to monitor applications,</li><li>a way to develop and spin up test environments,</li><li>supporting CLI tools, API&apos;s and UI&apos;s where appropriate</li></ul><h3 id="arent-we-already-doing-this">Aren&apos;t we already doing this?</h3><p>Yes, we are, but as you might have seen and experienced, building and maintaining these things can be somewhat <strong>complex and time consuming</strong>. If possible, I, as an engineer working on a product for the end customer, would love to spend more time on that product instead of building and maintaining the supporting platform. </p><p>Well, what if we applied software engineering principles to this problem? Welcome to the problem space of platform engineers. </p><p>Platform engineers apply <strong>software engineering principles</strong> like <strong>reusability, modularization, simplicity, security, resilience</strong>, etc. combined with <strong>product design principles</strong> in order to deliver easy to use products and services that other engineers can use to deliver value to end customers faster, easier and safer.</p><h3 id="is-platform-engineering-a-new-thing">Is Platform Engineering A New Thing?</h3><p>Other than the terminology itself, I don&apos;t think there there is much new here. I think it heavily depends on the scale of the organization. Amazon, Google, Facebook, etc. had internal teams building products for other internal teams for a long time. What is new is that the idea is going mainstream.</p><h3 id="note-1">Note</h3><p>I won&apos;t talk about it in this article, but a strong feedback and collaboration loop between product builders and customers is assumed. Otherwise the product will probably fail.</p><h2 id="conclusion">Conclusion</h2><p>Is platform engineering really a thing? I think it is. Staying competitive in the market is getting ever more difficult and complex. Software development, DevOps, site reliability engineering and now, platform engineering are all adjustments triggered by us trying to deal with the complexity, stay competitive and deliver value to customers faster.</p><p>Is platform engineering a good thing? Yes, but only if the platform products stay focused on the customer, remove obstacles, make them move faster, etc.</p><p></p>]]></content:encoded></item><item><title><![CDATA[Things To Keep In Mind As A Software Developer]]></title><description><![CDATA[There are many things a software developer could think of and spend their time with but there are only a few really important ones. These are some I think are important.]]></description><link>https://adnan-m.me/things-to-keep-in-mind-as-a-software-developer/</link><guid isPermaLink="false">661a3f55ce1dbddfec6c1861</guid><category><![CDATA[software engineering]]></category><category><![CDATA[communication]]></category><category><![CDATA[soft skills]]></category><category><![CDATA[advice]]></category><category><![CDATA[software development]]></category><dc:creator><![CDATA[Adnan Mujkanovic]]></dc:creator><pubDate>Wed, 17 Apr 2024 16:39:33 GMT</pubDate><media:content url="https://adnan-m.me/content/images/2024/04/things-to-keep-in-mind-as-a-software-developer-1.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: html--><div id="contents"></div><!--kg-card-end: html--><h2 id="intro">Intro</h2><img src="https://adnan-m.me/content/images/2024/04/things-to-keep-in-mind-as-a-software-developer-1.jpg" alt="Things To Keep In Mind As A Software Developer"><p>There are many things a software developer could think of and spend their time with but there are only a few really important ones. These are some I think are important.</p><h2 id="find-the-purpose">Find The Purpose</h2><p>If you don&apos;t have a goal, then you can never reach it. Before doing any work, you need to be clear about what it is you want to achieve, what is the purpose of the piece of software you want to write?</p><h2 id="look-for-clarity">Look For Clarity</h2><p>If you are not completely clear about the goal, you have to ask questions until you reach clarity and understanding of the problem i.e. the goal. Very often, grasping the problem correctly is 90% of the task.</p><h2 id="focus">Focus</h2><p>Now that you have a solid grasp of the goal, you need to focus on the solution. Don&apos;t start other tasks in between. Don&apos;t work on anything else other than your task except if there is a high priority emergency.</p><h2 id="be-empathic">Be Empathic</h2><p>Always have the customer / user in mind. What do they want to achieve? How are they going to use the feature you are building? Try to put yourself in their shoes and see things from their perspective. This also applies to yourself in the future and your colleagues. Can I write my code a bit more descriptive so it&apos;s easier to understand?</p><h2 id="break-it-down">Break It Down</h2><p>The vast majority of solutions consist of more than one line of code i.e. they consist of multiple steps. You cannot swallow a whole apple at once. In the same sense, you cannot not finish a task in one step. That&apos;s why your first task is to try and break things down into smaller steps.</p><h2 id="find-the-shortest-distance">Find The Shortest Distance</h2><p>The shortest distance between two points is a straight line. Your line might not be exactly straight but you have to focus on connecting A and B in the fastest way you can. Don&apos;t bother with elegance, best practices, design patterns, etc. Just connect the dots in the fastest way possible even if it&apos;s &quot;dirty&quot;.</p><h2 id="be-pragmatic">Be Pragmatic</h2><p>Software has a lifecycle and is not finished in X amount of days. Rather, software is written in iterations. Pragmatism is about getting the job done in the simplest and fastest way without bothering about theoretical purity. Get the job done first then you can make it better in future iterations.</p><h2 id="keep-it-simple-stupid">Keep It Simple, Stupid</h2><p>Sometimes, our brains lead us to weird places and we start writing overly complicated solutions. We have to stay vigilant, try and notice when this happens. In case it happens, we need to stop and think about the problem again and ask &quot;Is this really the simplest solution&quot;?</p><h2 id="ask-for-help">Ask For Help</h2><p>If you are stuck and not sure how to continue, ask for help and guidance. Find someone you think might be able to help you, tell them about your task and what you are trying to achieve. Tell them what you tried already and ask if they have an idea. Often, you will solve the issue just by describing it to someone.</p><h2 id="think-about-things-that-could-go-wrong">Think About Things That Could Go Wrong</h2><p>Murphy&apos;s law states: &quot;Anything that can go wrong will go wrong&quot;. And it will probably go wrong at the worst possible time. Unlimited things can go wrong and we cannot think nor defend against all of them but being aware of them and tackling some of them, can make our software much more resilient and our customers happier.</p><h2 id="review-your-code">Review Your Code</h2><p>After you connected A and B and you have validated that your idea / solution works, go back and read what you wrote. I bet you will find mistakes, or things you might have forgotten to consider.</p><h2 id="ask-others-to-review-your-code">Ask Others To Review Your Code</h2><p>No matter how smart you might be, your brain can never encompass the complexity of the world. But adding a few more brains might help in making our solution a bit better and catch missing pieces.</p><h2 id="be-open-minded">Be Open Minded</h2><p>Your ideas and solutions might seem amazing to you, and maybe they really are, but no matter how much you might love your idea, you have to leave some space for the possibility of other ideas being better. Also, your idea might have a critical flaw that you didn&apos;t think of yet and someone will point it out to you. In that case you should be thankful.</p><h2 id="consider-trade-offs">Consider Trade-Offs</h2><p>Trade-offs are situations where you have multiple, mutually exclusive, choices. One choice gives you a certain benefit but also comes with certain downsides. Another choice might not have the downsides of the first one, but then it also doesn&apos;t have the benefit. For example, one solution has very high performance, but not many features. Another solution has many features, but low performance. Which one you will choose, depends on your context, and what is important for your project at that time. Often, software development is balancing act.</p><h2 id="always-stay-a-junior">Always Stay A &quot;Junior&quot;</h2><p>A junior developer doesn&apos;t have an ego that might prevent them from asking questions, asking for help, asking about and listening to other peoples opinions and advice. A junior developer knows that there is so much they don&apos;t know yet and thus they are always ready to learn and expand their knowledge. By staying a junior in this way, you will never stop getting better at what you do.</p><h2 id="dont-reinvent-the-wheel">Don&apos;t Reinvent The Wheel</h2><p>Don&apos;t waste time solving problems that are already solved. If your business is selling flowers, don&apos;t be inveting a new JavaScript Framework. Work on providing your customer with the best choice of flowers and the easiest and most compfortable way for them to have the flowers delivered to them.</p><h2 id="separate-concerns">Separate Concerns</h2><p>Imagine one person in a company being responsible for, sales, marketing, accounting, software development, shipping, etc. Does that make sense? Is that possible? Not really. That&apos;s why companies have departments and roles with different concerns and responsibilities. You need to break down your software in pretty much the same way and while working on new features, keep those concerns in mind.</p><h2 id="think-ask-research">Think, Ask, Research ...</h2><p>Writing the actual code takes up, maybe, somewhere between 1%-10% of our job. The other 90%+ are about talking with team members, asking questions, sitting there, looking into the void, thinking and researching. The more you do of this, the better you will be at software development.</p><h2 id="be-a-team-player">Be A Team Player</h2><p>Talk with your colleagues, be honest, say what&apos;s on your mind, be effective and clear in your communication, be someone your colleagues can rely on, be open-minded, be kind, be respectful, give credit where it&apos;s due, etc.</p><h2 id="conclusion">Conclusion</h2><p>These are some fundamental things I think are important to keep in mind while developing software. There are, of course, many more depending on your role and context. </p>]]></content:encoded></item><item><title><![CDATA[User Auth? Sessions? Tokens? WTH?]]></title><description><![CDATA[User authentication is a requirement for almost any web application but how much do we know about it? Should we use classical sessions or modern tokens?]]></description><link>https://adnan-m.me/how-to-do-user-auth-with-sessions-or-tokens/</link><guid isPermaLink="false">650e8da727d361055a4d505d</guid><category><![CDATA[authentication]]></category><category><![CDATA[databases]]></category><category><![CDATA[scalability]]></category><category><![CDATA[api's]]></category><category><![CDATA[distributed systems]]></category><category><![CDATA[jwt]]></category><dc:creator><![CDATA[Adnan Mujkanovic]]></dc:creator><pubDate>Sun, 15 Oct 2023 12:18:30 GMT</pubDate><media:content url="https://adnan-m.me/content/images/2023/10/user-authentication.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: html--><div id="contents"></div><!--kg-card-end: html--><h2 id="intro">Intro</h2><img src="https://adnan-m.me/content/images/2023/10/user-authentication.jpg" alt="User Auth? Sessions? Tokens? WTH?"><p>If we want to write or work with almost any web application today, we will have to implement or deal with authentication and authorization. How do we authenticate users? How to make it secure and scalable?</p><p>There is a lot of confusing information online about these topics. This article is my attempt at clarifying the confusion.</p><h2 id="heads-up">Heads-Up</h2><p>I assume some familiarity with topics related to authentication.</p><p>I am not explaining individual token structures in detail. For that you can refer to <a href="https://jwt.io/introduction">this</a> (JWT), <a href="https://oauth.net/2/access-tokens/">this</a> (access/refresh tokens) and <a href="https://openid.net/specs/openid-connect-core-1_0.html#IDToken">this</a> (openid token).</p><p>When I refer to a &quot;client&quot; in this article, I mostly mean a web browser, not a mobile device. If you are interested in storing tokens securely on mobile devices, consider looking into <a href="https://developer.android.com/privacy-and-security/keystore">KeyStore</a> (android) or <a href="https://developer.apple.com/documentation/security/keychain_services/">KeyChain</a> (iOS). </p><h2 id="what-are-the-available-solutions">What are the available solutions?</h2><p>There are a few options for auth on the web but we&apos;ll take a look at a couple popular ones:</p><ul><li>Session based</li><li>Token based</li></ul><h3 id="security-measures">Security Measures</h3><p>Some security measures should be in-place for any auth architecture:</p><ul><li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies">HTTP-only cookies</a></li><li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#strict">Strict / Lax same-site cookies</a></li><li><a href="https://www.cloudflare.com/learning/ssl/what-is-https/">Encrypted communication / HTTPS</a> (<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#secure">secure</a>)</li><li><a href="https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#token-based-mitigation">CSRF tokens</a></li></ul><h2 id="what-is-session-based-authentication">What is session based authentication?</h2><p>Session based authentication is a process where a user exchanges login credentials for a session ID with a server / backend. The process looks something like this:</p><h3 id="how-does-it-work">How does it work?</h3><ol><li>The user fills a form with login credentials e.g. email and password and clicks on a submit button.</li><li>The backend receives the request and validates if a user with that email and password (<a href="https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#password-hashing-algorithms">hashed</a>) exists in the database.</li><li>If the user exists, an ID is generated and stored as a record in the database with additional details like expiry, the associated user, etc.</li><li>This ID is then sent back to the client, usually as an <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie">HTTP set-cookie response header</a>. </li><li>The browser then stores that ID as a cookie in its cookies storage and uses it for subsequent requests to the backend.</li><li>The backend then authenticates subsequent requests using the session ID and its associated record in the database instead of an email and password.</li></ol><h3 id="security-considerations">Security considerations</h3><p>When using session based authentication you should be aware of potential security issues like:</p><ul><li><a href="https://portswigger.net/web-security/csrf/xss-vs-csrf">XSS</a></li><li><a href="https://portswigger.net/web-security/csrf/xss-vs-csrf">CSRF</a></li><li><a href="https://owasp.org/www-community/attacks/Session_hijacking_attack">Session Hijacking</a></li></ul><p>For mitigation and prevention of these refer to the section above - <a href="#security-measures">&quot;Security Measures&quot;</a></p><h3 id="pros">Pros</h3><ul><li>Simple and easy to implement</li><li>Most backend languages and frameworks do most of the work for you</li><li>Automatic expiration</li><li>Easy way of blocking / revoking / restricting access for specific users</li><li>Battle tested</li></ul><h3 id="cons">Cons</h3><ul><li>Requires some sort of persistence</li><li>Cookies (sessions) bound to one domain</li><li>Requires optimization and scaling out of storage at large scale, but what solution doesn&apos;t?! :D</li></ul><h3 id="summary">Summary</h3><p>Session based authentication has been used successfully for a long time and securely for those who had security on their minds. It&apos;s simple and also works great up to a certain scale (covers probably 99.9% of use cases). However, when you reach a certain scale, session persistence might become a bottleneck and you will have to start optimizing and scaling it out. </p><h2 id="what-is-token-based-authentication">What is token based authentication?</h2><p>What if I told you, you can authenticate users at massive scale without having a session storage? I&apos;d probably be lying. But if you need an extremely complicated solution that will still need persistence then tokens might be a good fit.</p><h3 id="how-does-basic-jwt-auth-work">How does basic JWT auth work?</h3><p><strong>Step 3</strong> is where the path changes compared to session auth.</p><ol><li>The user fills a form with login credentials e.g. email and password and clicks on a submit button.</li><li>The auth service receives the request and validates if a user with that email and password (<a href="https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#password-hashing-algorithms">hashed</a>) exists in the database.</li><li>The auth service generates a JWT (JSON web token) that contains basic user information of our choosing and optionally an expiration timestamp.</li><li>The auth service signs the JWT with a private token / secret that is used across the distributed system to validate JWT authenticity.</li><li>The auth service sends the token back to the client via an <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies">HTTP-only cookie</a> (up to 4096 bytes in size).</li><li>All subsequent requests will contain the cookie i.e. JWT token so the backend will be able to appropriately authenticate and authorize the request based on data contained in the JWT token.</li></ol><p>Voila! We have distributed authentication and authorization that can be used across a distributed system without checking the database every time we do the auth.</p><p>This is the most basic auth flow using JWT tokens.</p><p>...</p><figure class="kg-card kg-image-card"><img src="https://adnan-m.me/content/images/2023/10/few-moments-later.jpg" class="kg-image" alt="User Auth? Sessions? Tokens? WTH?" loading="lazy" width="1920" height="810" srcset="https://adnan-m.me/content/images/size/w600/2023/10/few-moments-later.jpg 600w, https://adnan-m.me/content/images/size/w1000/2023/10/few-moments-later.jpg 1000w, https://adnan-m.me/content/images/size/w1600/2023/10/few-moments-later.jpg 1600w, https://adnan-m.me/content/images/2023/10/few-moments-later.jpg 1920w" sizes="(min-width: 720px) 720px"></figure><p>Colleague: &quot;I see suspicious activity in the logs coming from a specific user... Can we block this user?&quot;</p><p>Me who implemented basic JWT auth: &quot;Sure we can! We delete the user in the user database and just change the secret we use in all the services to verify JWT tokens.&quot;</p><p>Colleague: &quot;Oh, great! But won&apos;t that force all other users to re-login?&quot;</p><p>Me: &quot;Yes. Is that a problem?&quot;</p><p>It might not be an issue in your use case but if it is ... Well, this is where the fun part begins :D</p><h3 id="the-problem-of-token-invalidation-revocation">The Problem of Token Invalidation / Revocation</h3><p>As you &#xA0;can see, in case of malicious user activity or compromised tokens, we are not able to block or limit a targeted user without invalidating tokens of all other users forcing them to re-login.</p><p>We cannot solve this issue without introducing additional state and checks.</p><h3 id="the-problem-of-stale-tokens">The Problem of Stale Tokens</h3><p>As mentioned previously, JWT tokens contain a small amount of data related to the user like email, age, role etc. But if the JWT token never expires or has long validity, how do we update the JWT token data?</p><p>We cannot update the data if we don&apos;t ask for the JWT token again with our username and password.</p><h3 id="what-are-refresh-and-access-tokens">What are refresh and access tokens?</h3><p>Now we are aware of a couple shortcomings of basic JWT token auth. Are there solutions?</p><p>If you thought, &quot;Wait, I think we need more tokens!&quot;, you thought right.</p><p>To solve the issues of revocation, stale data and long lived (insecure) tokens we will split our basic JWT token mentioned above, into two tokens - <a href="https://oauth.net/2/refresh-tokens/">the refresh token</a> and <a href="https://oauth.net/2/access-tokens/">the access token</a> defined in the <a href="https://oauth.net/2/">OAuth 2.0 spec</a>.</p><p>The access token is short lived (e.g. 10 min) and will be used for protected resource requests (e.g. /orders)</p><p>The refresh token is used solely for obtaining new access tokens and cannot be used to access protected resources.</p><h3 id="how-do-refresh-and-access-tokens-work">How do refresh and access tokens work?</h3><ol><li>The user fills a form with login credentials e.g. email and password and clicks on a submit button.</li><li>The auth service receives the request and validates if a user with that email and password (<a href="https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#password-hashing-algorithms">hashed</a>) exists in the database.</li><li>The auth service generates a refresh JWT token and a access JWT token and sends them back to the client.</li><li>The client uses the short lived (e.g. 10 min) access token for subsequent protected resource requests.</li><li>The client uses the refresh token to get another access token after the current one expires.</li></ol><h3 id="what-are-the-benefits-of-a-refresh-access-token-flow">What are the benefits of a refresh / access token flow</h3><ul><li>Forcing the client to ask for access tokens every e.g. 10 min, solves both the revocation and stale data issue.</li><li>After we block or limit a specific user, the next access token request will fail stopping or limiting the users activity.</li><li>Data contained in the tokens can also not really become stale since we are asking for it every 10 min.</li><li>We still keep the stateless benefit of our tokens when using it with the other services in the distributed system since they</li></ul><h3 id="note-on-refresh-token-expiration">Note On Refresh Token Expiration</h3><p>With a dedicated, full-featured auth service and clients are forced to ask for access tokens periodically, I don&apos;t see a reason to have long-lived refresh tokens. </p><p>Instead I recommend invalidating and issuing new refresh tokens every time the client asks for a new access token.</p><p>Voila! Problems solved.</p><p>...</p><figure class="kg-card kg-image-card"><img src="https://adnan-m.me/content/images/2023/10/few-moments-later-1.jpg" class="kg-image" alt="User Auth? Sessions? Tokens? WTH?" loading="lazy" width="1920" height="810" srcset="https://adnan-m.me/content/images/size/w600/2023/10/few-moments-later-1.jpg 600w, https://adnan-m.me/content/images/size/w1000/2023/10/few-moments-later-1.jpg 1000w, https://adnan-m.me/content/images/size/w1600/2023/10/few-moments-later-1.jpg 1600w, https://adnan-m.me/content/images/2023/10/few-moments-later-1.jpg 1920w" sizes="(min-width: 720px) 720px"></figure><p>Worried and puzzled colleague: &quot;Multiple panicked customers contacted us saying that someone else gained access to their accounts and deleted all their data.&quot;</p><p>Me who suggested we use refresh and access tokens: &quot;OMG! That&apos;s a nightmare for our business. How could they gain access? Wait ... Where are we storing the tokens on the client side?&quot;</p><p>Colleague: &quot;Yes, I asked the same question. We are storing the tokens in local browser storage.&quot;</p><p>Me: &quot;Local storage!? That&apos;s a severe security risk!&quot;</p><h3 id="where-to-store-tokens-securely">Where to store tokens securely?</h3><p>There are a few options for storing tokens:</p><ul><li>Local storage </li><li>Session storage</li><li>Regular cookies</li><li>Browser memory</li><li>HTTP-only / secure / lax same-site cookies (spoiler: this is the best one)</li></ul><p>So we have 2 or more types of tokens and 4 options for storage of the tokens. Let&apos;s go over the options ...</p><h3 id="storing-sensitive-tokens-in-local-storage-spoiler-dont">Storing Sensitive Tokens In Local Storage (Spoiler: DON&apos;T)</h3><p>Local storage is accessible by all JavaScript running in all browser tabs. This is horrific from a security perspective and disqualifying for sensitive data such as refresh and access tokens.</p><h3 id="storing-sensitive-tokens-in-session-storage-spoiler-dont">Storing Sensitive Tokens In Session Storage (Spoiler: DON&apos;T)</h3><p>Session storage is accessible by all JavaScript running in the same tab / page making it a little less horrific compared to local storage but still disqualifying as it is accessible by vendor JavaScript.</p><h3 id="storing-sensitive-tokens-as-regular-cookies-spoiler-dont">Storing Sensitive Tokens As Regular Cookies (Spoiler: DON&apos;T)</h3><p>Regular cookies (non-http-only) are accessible by all JavaScript running on the page. This is disqualifying for sensitive data such as refresh and access tokens.</p><h3 id="storing-sensitive-tokens-in-memory">Storing Sensitive Tokens In Memory</h3><p>Storing the access tokens in memory might be acceptable if your website is a single page application i.e. users are not changing/refreshing the page every minute. </p><p>Refresh tokens on the other hand cannot be stored in memory since we have to provide our credentials to get a refresh token i.e. we would have to re-login every time we change/refresh the page.</p><h3 id="storing-tokens-as-http-only-cookies">Storing Tokens As HTTP-Only Cookies</h3><p>Finally we arrive at the most secure option and my recommendation for optimal security. </p><p>Refresh and access tokens should be stored as HTTP-only / secure / strict (or lax) same-site cookies and not be accessible to JavaScript at all instead only allow us to call an endpoint for access token rotation.</p><p>Storing refresh and access tokens as HTTP-only cookies prohibits accessing data stored in the tokens e.g. username, email, role, expiration etc. But that is what we have ID tokens for.</p><h3 id="what-are-id-tokens">What are ID tokens?</h3><p>&quot;An ID token contains information about what happened when a user authenticated, and is intended to be read by the OAuth client. The ID token may also contain information about the user such as their name or email address, although that is not a requirement of an ID token.&quot; - <a href="https://oauth.net/id-tokens-vs-access-tokens/">OAuth 2.0 Spec</a></p><p>The ID token is not used for auth against protected API resources. </p><p>Since this token is intended to be read by the client, contains information about the auth request and can contain basic user information, it is perfect for at least a couple use cases:</p><ul><li>Enhance user experience with e.g. a username displayed somewhere on the page or display a basic user profile / card on the page</li><li>Store access token expiration so the client can request a new one on time</li></ul><p>If your page is a SPA I&apos;d recommend storing the ID token in memory and refreshing it every time you request a new access token from the auth service.</p><p>In case of a classical server rendered page, let the server deal with it and render the page with all relevant data on the server.</p><h3 id="pros-1">Pros</h3><ul><li>Services, other than auth, don&apos;t need to make database calls to authenticate users</li><li>Allows for auth decoupling</li><li>Allows the auth service to pick the fastest and most scalable DB technology</li></ul><h3 id="cons-1">Cons</h3><ul><li>High level of complexity</li></ul><h2 id="what-about-cookies-and-the-multi-domain-problem">What About Cookies And The Multi-Domain Problem?</h2><p>Let&apos;s get the easy solution out of the way, for sub-domains you can use <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#define_where_cookies_are_sent">the cookie domain attribute</a>.</p><p>The cookie domain attribute is, unfortunately, not a solution for different root domains e.g. sharing a cookies between xyz.com and zyx.com. This is not possible.</p><p>To solve this issue our auth service needs to be able to create very short-lived tokens (e.g. 10s) that can be consumed / authenticated by a different domain using a shared secret.</p><h3 id="the-flow">The Flow</h3><ol><li>User authenticates with credentials on auth.xyz.com</li><li>auth.xyz.com responds with refresh/access token as HTTP-only / strict (or lax) same site / secure cookies</li><li>The user is now able to access protected resources under the domain xyz.com</li><li>The user can now call an endpoint on the auth.xyz.com service e.g. /domain/zyx.com</li><li>auth.xyz.com will generate a very short-lived (10s) JWT token containing user identification information</li><li>auth.xyz.com will respond with a redirect pointing to e.g. auth.zyx.com/auth/token/[JWT-token]</li><li>auth.zyx.com will receive the authentication request with the token attached</li><li>auth.zyx.com will authenticate the token using a shared secret</li><li>auth.zyx.com will use the user identification contained in the token to generate a refresh and access token for that user</li><li>auth.zyx.com will return the refresh/access token as HTTP-only / strict (or lax) same site / secure cookies</li><li>The user is now able to access protected resources under zyx.com</li></ol><p>Note: The auth service doesn&apos;t have to be a completely different service. It can be one service that responds on multiple domains.</p><p>Note: The token that is used between two domains doesn&apos;t have to be a JWT token. However, the auth service receiving the token must be able to find the user based on the token.</p><h2 id="conclusion">Conclusion</h2><p>Maybe you have more questions about auth now than you had at the beginning. This indicates the complexity of the topic. But I hope I was able to shed some light on at least a few points. </p><p>Regarding security, there is no compromise here. We need to understand our security and it has to be as air-tight as possible. </p><p>Regarding complexity, that depends, as always, on your context and point in the journey. If you are starting off from zero, keep it as simple as possible without compromising security and adapt as requirements change.</p><p>PS: Apologies for spelling mistakes.</p><h2 id="references">References</h2><ul><li><a href="https://oauth.net/2/">OAuth 2.0 Specification</a></li><li><a href="https://openid.net/developers/specs/">OpenID Specification</a></li><li><a href="https://cheatsheetseries.owasp.org/">OWASP Cheatsheets</a></li><li><a href="https://jwt.io/introduction">JWT Introduction</a></li></ul>]]></content:encoded></item><item><title><![CDATA[Engineering Books, Blogs & Podcasts]]></title><description><![CDATA[A list of blogs and podcasts from the field of software architecture, development, engineering, etc.]]></description><link>https://adnan-m.me/engineering-books-blogs-and-podcasts/</link><guid isPermaLink="false">64cb450eca40150550b732ef</guid><category><![CDATA[podcast]]></category><category><![CDATA[software engineering]]></category><category><![CDATA[aws]]></category><category><![CDATA[google]]></category><category><![CDATA[architecture]]></category><category><![CDATA[books]]></category><category><![CDATA[blogs]]></category><dc:creator><![CDATA[Adnan Mujkanovic]]></dc:creator><pubDate>Sat, 05 Aug 2023 17:24:06 GMT</pubDate><media:content url="https://adnan-m.me/content/images/2023/08/book-blogs-and-podcasts.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: html--><div id="contents"></div><!--kg-card-end: html--><h2 id="books">Books</h2><img src="https://adnan-m.me/content/images/2023/08/book-blogs-and-podcasts.jpg" alt="Engineering Books, Blogs &amp; Podcasts"><p><a href="https://martinfowler.com/books/eaa.html">Patterns of Enterprise Application Architecture</a> - Martin Fowler</p><p><a href="https://www.goodreads.com/en/book/show/44919">Working Effectively with Legacy Code</a> - Michael C. Feathers </p><p><a href="https://www.domainlanguage.com/ddd/">Domain-Driven Design</a> - Eric Evans</p><p><a href="https://martinfowler.com/books/refactoring.html">Refactoring</a> - Martin Fowler, Kent Beck</p><p><a href="https://dataintensive.net/">Designing Data-Intensive Applications</a> - Martin Kleppmann</p><p><a href="https://sre.google/sre-book/table-of-contents/">Site Reliability Engineering</a> - Google</p><p><a href="https://www.goodreads.com/book/show/108986.Introduction_to_Algorithms">Introduction to Algorithms</a> - Thomas H. Cormen, Charles E. Leiserson, ...</p><p><a href="https://sandimetz.com/99bottles">99 Bottles of OOP</a> - Sandi Metz</p><p><a href="https://www.thriftbooks.com/w/inside-the-machine-an-illustrated-introduction-to-microprocessors-and-computer-architecture_jon-stokes/1629741/#edition=4458187&amp;idiq=31917135">Inside The Machine</a> - Jon Stokes</p><p><a href="https://www.goodreads.com/book/show/840.The_Design_of_Everyday_Things">The Design Of Everyday Things</a> - Donald A. Norman</p><p><a href="https://www.goodreads.com/it/book/show/67833">Extreme Programming</a> - Kent Beck</p><p><a href="https://www.goodreads.com/book/show/67825.Peopleware">Peopleware</a> - Tom DeMarco, Timothy Lister</p><p><a href="https://teamtopologies.com/book">Team Topologies</a> - Matthew Skelton, Manuel Pais</p><p><a href="https://noidea.dog/staff">Staff Engineer&apos;s Path</a> - Tanya Reilly</p><p><a href="https://itrevolution.com/product/accelerate/">Accelerate</a> - Nicole Forsgren, Jez Humble, Gene Kim</p><p><a href="https://www.amazon.com/Language-Emotions-Karla-McLaren-ebook/dp/B003X27LCM?_encoding=UTF8&amp;qid=&amp;sr=&amp;linkCode=sl1&amp;tag=threriveinst-20&amp;linkId=3bbca565ecbe8740b95ec5a9a5f496da&amp;language=en_US&amp;ref_=as_li_ss_tl">The Language of Emotions</a> - Karla McLaren</p><h2 id="blogs">Blogs</h2><p><a href="https://aws.amazon.com/blogs/?awsf.blog-master-category=*all&amp;awsf.blog-master-learning-levels=*all&amp;awsf.blog-master-industry=*all&amp;awsf.blog-master-analytics-products=*all&amp;awsf.blog-master-artificial-intelligence=*all&amp;awsf.blog-master-aws-cloud-financial-management=*all&amp;awsf.blog-master-blockchain=*all&amp;awsf.blog-master-business-applications=*all&amp;awsf.blog-master-compute=*all&amp;awsf.blog-master-customer-enablement=*all&amp;awsf.blog-master-customer-engagement=*all&amp;awsf.blog-master-database=*all&amp;awsf.blog-master-developer-tools=*all&amp;awsf.blog-master-devops=*all&amp;awsf.blog-master-end-user-computing=*all&amp;awsf.blog-master-mobile=*all&amp;awsf.blog-master-iot=*all&amp;awsf.blog-master-management-governance=*all&amp;awsf.blog-master-media-services=*all&amp;awsf.blog-master-migration-transfer=*all&amp;awsf.blog-master-migration-solutions=*all&amp;awsf.blog-master-networking-content-delivery=*all&amp;awsf.blog-master-programming-language=*all&amp;awsf.blog-master-sector=*all&amp;awsf.blog-master-security=*all&amp;awsf.blog-master-storage=*all">AWS Blog</a></p><p><a href="https://developers.googleblog.com/">Google for Developers</a></p><p><a href="https://github.blog/category/engineering/">GitHub Engineering Blog</a></p><p><a href="https://martinfowler.com/">MartinFowler.com</a></p><p><a href="https://substack.com/@kentbeck">Kent Beck</a></p><p><a href="https://stackoverflow.blog/">Stack Overflow Blog</a></p><p><a href="https://blog.cloudflare.com/tag/engineering/">Cloudflare Engineering Blog</a></p><p><a href="https://slack.engineering/">Slack Engineering</a></p><p><a href="https://netflixtechblog.com/">Netflix Tech Blog</a></p><p><a href="https://eng.uber.com/">Uber Engineering</a></p><p><a href="https://engineering.zalando.com/">Zalando Engineering</a></p><h2 id="podcasts">Podcasts</h2><p><a href="https://softwareengineeringdaily.com/">Software Engineering Daily</a></p><p><a href="https://sre.google/prodcast/">SRE Prodcast</a></p><p><a href="https://aws.amazon.com/developer/podcast/?developer-podcast.sort-by=item.additionalFields.EpisodeNum&amp;developer-podcast.sort-order=desc">AWS Developers</a></p><p><a href="https://fellow.app/supermanagers/category/engineering/">SuperManagers</a></p><p><a href="https://changelog.com/podcast">Changelog</a></p><p><a href="https://changelog.com/gotime">Go Time (Golang)</a></p>]]></content:encoded></item><item><title><![CDATA[How To Measure Time & Space Code Complexity?]]></title><description><![CDATA[The code, functions and algorithms we write are not all the same. One very important aspect in which they differ is the complexity and efficiency.]]></description><link>https://adnan-m.me/how-to-measure-code-complexity/</link><guid isPermaLink="false">64560ea6ca40150550b72b34</guid><category><![CDATA[computer science]]></category><category><![CDATA[algorithms]]></category><category><![CDATA[complexity]]></category><category><![CDATA[software engineering]]></category><dc:creator><![CDATA[Adnan Mujkanovic]]></dc:creator><pubDate>Sun, 21 May 2023 13:04:48 GMT</pubDate><media:content url="https://adnan-m.me/content/images/2023/05/big-o-notation.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: html--><div id="contents"></div><!--kg-card-end: html--><h2 id="intro-motivation-why">Intro / Motivation / Why</h2><img src="https://adnan-m.me/content/images/2023/05/big-o-notation.jpg" alt="How To Measure Time &amp; Space Code Complexity?"><p>The code, functions and algorithms we write are not all the same. One very important aspect in which they differ is the <strong>complexity</strong> and <strong>efficiency</strong>. In order to <strong>produce quality solutions</strong> and <strong>improve</strong> existing ones, it would be great if there was a way to measure this somehow. Is there a way to measure this difference mathematically? Yes! We do this using the &quot;<strong>Big O Notation</strong>&quot;</p><h2 id="what-is-big-o-notation">What is &quot;Big O Notation&quot;?</h2><p>Now that we have the why out of the way, let&apos;s look at the &quot;what&quot;. Skipping the, usually, confusing wikipedia definition, let&apos;s try something simpler...</p><p>Big O notation is a <strong>mathematical description</strong> of algorithmic <strong>complexity</strong> relative to <strong>time </strong>and<strong> space</strong> i.e. a description of how much time or resources an algorithm will consume with increasing input size.</p><h2 id="how-does-big-o-break-down-complexity">How Does Big O Break Down Complexity?</h2><p>Big O notation breaks complexity down by the amount of <strong>time and space</strong> an algorithm uses relative to the <strong>input</strong>. An algorithm then falls into one of the following notations:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://adnan-m.me/content/images/2023/05/big-o-notation-break-down.jpeg" class="kg-image" alt="How To Measure Time &amp; Space Code Complexity?" loading="lazy" width="800" height="556" srcset="https://adnan-m.me/content/images/size/w600/2023/05/big-o-notation-break-down.jpeg 600w, https://adnan-m.me/content/images/2023/05/big-o-notation-break-down.jpeg 800w" sizes="(min-width: 720px) 720px"><figcaption>https://www.bigocheatsheet.com/</figcaption></figure><p>So our algorithms can be have one of the following complexities:</p><h3 id="1-o1">1. O(1)</h3><p>Time or space is <strong>independent</strong> of input size e.g.:</p><p>- input 2 - time <strong>1</strong><br>- input 4 - time <strong>1</strong><br>- input 8 - time <strong>1</strong></p><h3 id="2-olog-n">2. O(log n)</h3><p>Time or space increases based on every <strong>doubling</strong> of input size e.g.: </p><p>- input 2 - time <strong>2</strong>, <br>- input 4 - time <strong>3</strong>, <br>- input 8 - time <strong>4</strong></p><h3 id="3-on">3. O(n)</h3><p>Time or space increase is directly <strong>tied</strong> the input size e.g.:</p><p>- input 2 - time <strong>2</strong>, <br>- input 4 - time <strong>4</strong>, <br>- input 8 - time <strong>8</strong></p><h3 id="4-on-log-n">4. O(n log n)</h3><p>Time or space <strong>doubles</strong> as input size increases e.g.:</p><p>- input 2 - time <strong>4</strong>,<br>- input 4 - time <strong>8</strong>,<br>- input 8 - time <strong>16</strong></p><h3 id="5-on2">5. O(n^2)</h3><p>Time or space increases <strong>quadratically</strong> as input size increases e.g.:</p><p>- input 2 - time <strong>4</strong>,<br>- input 4 - time <strong>16</strong>,<br>- input 8 - time <strong>64</strong></p><h3 id="6-o2n">6. O(2^n)</h3><p>Time or space increases <strong>exponentially</strong> as input size increases e.g.:</p><p>- input 2 - time <strong>4</strong>,<br>- input 4 - time <strong>16</strong>,<br>- input 8 - time <strong>256</strong></p><h3 id="7-on">7. O(n!)</h3><p>Time or space increases <strong>factorially</strong> as input size increases e.g.:</p><p>- input 2 - time <strong>2</strong>,<br>- input 4 - time <strong>24</strong>,<br>- input 8 - time <strong>40320</strong></p><p>As we can see, the different complexities can have <strong>dramatic impact</strong>.</p><p><strong>NOTE:</strong> Algorithms are <strong>not limited</strong> to one of the above but can have <strong>multiple complexities</strong> depending on the input. This means they can have a <strong>best case</strong>, an <strong>average</strong> and a <strong>worst case</strong> scenario.</p><h2 id="show-me-the-code">Show Me The Code!</h2><h3 id="first-element">First Element</h3><p>Let&apos;s take the following example (Javascript) ...</p><!--kg-card-begin: markdown--><pre><code>function getFirstElement(array) {
  return array[0];
}
</code></pre>
<!--kg-card-end: markdown--><p>This is an <strong>O(1)</strong> complexity because the time required for the execution is <strong>independent of the input</strong> array length. Unfortunately, only a few algorithms can be this fast.</p><h3 id="bubble-sort">Bubble Sort</h3><p>Let&apos;s take another example (Javascript) ...</p><!--kg-card-begin: markdown--><pre><code>function bubbleSort(array) {
  while (true) {
    let swapped = false;
    for (let i = 0; i &lt; array.length; i++) {
      // if next element is bigger swap
      // the current with the next
      const nextElement = array[i+1]
      if(nextElement != undefined &amp;&amp; array[i] &gt; nextElement) {
        const temp = array[i]
        array[i] = nextElement;
        array[i+1] = temp;
        swapped = true;
      }
    }
    
    // finish if there was no swap after
    // a full traversal over all elements
    if (! swapped) {
      break;
    }
  }
  
  return array;
}
</code></pre>
<!--kg-card-end: markdown--><p>This bubble sort algorithm iterates over an array over and over again comparing the current and next element in the array. If the current is bigger than the next, it swaps the two. It does this until it doesn&apos;t detect a swap after a full input array iteration.</p><p>Let&apos;s visualize it for better understanding ...</p><p><strong>First</strong> full traversal of the input array:</p><p>[<strong>6, 4</strong>, 5, 3, 2, 1] &#x2192; [<strong>4, 6</strong>, 5, 3, 2, 1] - swap<br>[4, <strong>6, 5</strong>, 3, 2, 1] &#x2192; [4, <strong>5, 6</strong>, 3, 2, 1] - swap<br>[4, 5, <strong>6, 3</strong>, 2, 1] &#x2192; [4, 5, <strong>3, 6</strong>, 2, 1] - swap<br>[4, 5, 3, <strong>6, 2</strong>, 1] &#x2192; [4, 5, 3,<strong> 2, 6</strong>, 1] - swap<br>[4, 5, 3,<strong> </strong>2,<strong> 6, 1</strong>] &#x2192; [4, 5, 3,<strong> </strong>2,<strong> 1, 6</strong>] - swap</p><p>swapped = true</p><p><strong>Second</strong> full traversal of the input array:</p><p>[<strong>4, 5</strong>, 3,<strong> </strong>2,<strong> </strong>1, 6] &#x2192; [<strong>4, 5</strong>, 3,<strong> </strong>2,<strong> </strong>1, 6]<br>[4, <strong>5, 3</strong>,<strong> </strong>2,<strong> </strong>1, 6] &#x2192; [4, <strong>3, 5</strong>,<strong> </strong>2,<strong> </strong>1, 6] - swap <br>[4, 3, <strong>5, 2</strong>,<strong> </strong>1, 6] &#x2192; [4, 3, <strong>2, 5</strong>,<strong> </strong>1, 6] - swap<br>[4, 3, 2, <strong>5, 1</strong>, 6] &#x2192; [4, 3, 2, <strong>1, 5</strong>, 6] - swap<br>[4, 3, 2, 1, <strong>5, 6</strong>] &#x2192; [4, 3, 2, 1, <strong>5, 6</strong>]</p><p>swapped = true</p><p>...</p><p>This goes on until we have a full traversal without any swaps ...</p><p>[<strong>1, 2</strong>, 3, 4, 5, 6] &#x2192; [<strong>1, 2</strong>, 3, 4, 5, 6]<br>[1, <strong>2, 3</strong>, 4, 5, 6] &#x2192; [1, <strong>2, 3</strong>, 4, 5, 6]<br>[1, 2, <strong>3, 4</strong>, 5, 6] &#x2192; [1, 2, <strong>3, 4</strong>, 5, 6]<br>[1, 2, 3, <strong>4, 5</strong>, 6] &#x2192; [1, 2, 3, <strong>4, 5</strong>, 6]<br>[1, 2, 3, 4, <strong>5, 6</strong>] &#x2192; [1, 2, 3, 4, <strong>5, 6</strong>]</p><p><strong>swapped = false</strong></p><p>So what is the complexity of this? For the sake of brevity we&apos;ll only determine the worst case scenario.</p><p>If we use the input [8, 7, 6, 5, 4, 3, 2, 1] and build in an iterations counter we will see that for an <strong>input of 8</strong> we the algorithm uses <strong>64 iterations</strong>.</p><p>If we check the previously mentioned big O notations we will see that <strong>O(n^2)</strong> gives us a time complexity of 64 for an input of 8.</p><h2 id="is-big-o-always-decisive">Is Big O Always Decisive?</h2><p>You know how our physics rules don&apos;t work on the quantum scale? Well Big O is kinda similar i.e. for small inputs one algorithm can seemingly be much faster than another but do we really care if something took 1ms or 5ms? We might, in rare cases but what really matters for the vast majority of us is the difference between e.g. 500ms and 4s. So Big O is much more useful for the bigger scale and larger inputs rather then small inputs where some algorithms can be faster than others but then break down and take much more time for larger inputs.</p><h2 id="conclusion">Conclusion</h2><p>The Big O notation is a good tool that every software engineer should be aware of. It helps us describe algorithm complexity and as such it can be beneficial to keep it in mind when writing and optimizing our solutions.</p>]]></content:encoded></item><item><title><![CDATA[Naming In OOP And "ER/OR" Endings]]></title><description><![CDATA[<p>We all heard that naming is one of the hardest problems in software development. Whoever did some HTML/CSS, OOP, etc. can attest to the struggle of coming up with good names for classes, functions, variables, etc.</p><p>Question is why do we need good names? Well we need good names</p>]]></description><link>https://adnan-m.me/naming-in-oop-and-er-or/</link><guid isPermaLink="false">64220a0b61c818057336720a</guid><category><![CDATA[OOP]]></category><category><![CDATA[naming]]></category><category><![CDATA[architecture]]></category><dc:creator><![CDATA[Adnan Mujkanovic]]></dc:creator><pubDate>Tue, 28 Mar 2023 23:33:44 GMT</pubDate><media:content url="https://adnan-m.me/content/images/2023/03/oop-naming-or-er.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://adnan-m.me/content/images/2023/03/oop-naming-or-er.jpg" alt="Naming In OOP And &quot;ER/OR&quot; Endings"><p>We all heard that naming is one of the hardest problems in software development. Whoever did some HTML/CSS, OOP, etc. can attest to the struggle of coming up with good names for classes, functions, variables, etc.</p><p>Question is why do we need good names? Well we need good names so we are able to reason easier about the code which in turn makes modification and extension much easier.</p><p>Unfortunately, because it is a hard problem, we often get it wrong. One of the ways we get it wrong, in my opinion, is when we are giving fake nouns to things instead of <strong>proper nouns</strong>.</p><p>Without going into a big discussion and detail, I will just try to illustrate the issue in the shortest most obvious way and let you decide.</p><p>Let&apos;s take this example ...</p><figure class="kg-card kg-image-card"><img src="https://adnan-m.me/content/images/2023/03/oop-naming.jpg" class="kg-image" alt="Naming In OOP And &quot;ER/OR&quot; Endings" loading="lazy" width="1200" height="600" srcset="https://adnan-m.me/content/images/size/w600/2023/03/oop-naming.jpg 600w, https://adnan-m.me/content/images/size/w1000/2023/03/oop-naming.jpg 1000w, https://adnan-m.me/content/images/2023/03/oop-naming.jpg 1200w" sizes="(min-width: 720px) 720px"></figure><p>If I gave you these boxes and told you &quot;build me something&quot;, will you be able to easily reason about the boxes and very quickly be able to build something that makes sense using them?</p><p>Another example ...</p><figure class="kg-card kg-image-card"><img src="https://adnan-m.me/content/images/2023/03/oop-naming-2.jpg" class="kg-image" alt="Naming In OOP And &quot;ER/OR&quot; Endings" loading="lazy" width="1200" height="600" srcset="https://adnan-m.me/content/images/size/w600/2023/03/oop-naming-2.jpg 600w, https://adnan-m.me/content/images/size/w1000/2023/03/oop-naming-2.jpg 1000w, https://adnan-m.me/content/images/2023/03/oop-naming-2.jpg 1200w" sizes="(min-width: 720px) 720px"></figure><p>Will you be able to easily reason about these objects and come up with ideas of how to build something that makes sense with them and also easily find what other objects might be needed?</p><p>My prediction is that the answers to the questions above is &quot;Yes. I will be able to easily reason about and work with these objects&quot;</p><p>Now let&apos;s look at another example ...</p><figure class="kg-card kg-image-card"><img src="https://adnan-m.me/content/images/2023/03/bad-oop-naming-2.jpg" class="kg-image" alt="Naming In OOP And &quot;ER/OR&quot; Endings" loading="lazy" width="1200" height="600" srcset="https://adnan-m.me/content/images/size/w600/2023/03/bad-oop-naming-2.jpg 600w, https://adnan-m.me/content/images/size/w1000/2023/03/bad-oop-naming-2.jpg 1000w, https://adnan-m.me/content/images/2023/03/bad-oop-naming-2.jpg 1200w" sizes="(min-width: 720px) 720px"></figure><p>Now the same question, If I gave you these boxes and told you &quot;build me something&quot;, will you be able to easily reason about the boxes and very quickly be able to build something using them?</p><p>My prediction is you will not be able to easily reason about these objects and you will have very hard and uncomfortable time building something that makes sense with them. There are, of course, some exception to this but I hope that I got the point across.</p><p>There you go. That&apos;s my beef with &quot;ER/OR&quot; classes in OOP.</p><p>There are many opinions and articles on this topic but I would say if you are interested and have some spare time on your hands you can read through this delightful article / story ...</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Stevey&#x2019;s Blog Rants: Execution in the Kingdom of Nouns</div><div class="kg-bookmark-description"></div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.blogger.com/favicon.ico" alt="Naming In OOP And &quot;ER/OR&quot; Endings"><span class="kg-bookmark-author">Stevey&apos;s Blog Rants</span><span class="kg-bookmark-publisher">Steve Yegge</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://resources.blogblog.com/img/icon18_edit_allbkg.gif" alt="Naming In OOP And &quot;ER/OR&quot; Endings"></div></a></figure>]]></content:encoded></item><item><title><![CDATA[Assumptions In Communication]]></title><description><![CDATA[<!--kg-card-begin: html--><div id="contents"></div><!--kg-card-end: html--><h2 id="the-problem">The Problem</h2><p>While communicating with other people, we might say a lot or hear a lot. Despite a large amount of available information during communication, a lot is still left out leaving gaps in the picture.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://adnan-m.me/content/images/2023/03/gaps-in-communication.jpg" class="kg-image" alt loading="lazy" width="1280" height="300" srcset="https://adnan-m.me/content/images/size/w600/2023/03/gaps-in-communication.jpg 600w, https://adnan-m.me/content/images/size/w1000/2023/03/gaps-in-communication.jpg 1000w, https://adnan-m.me/content/images/2023/03/gaps-in-communication.jpg 1280w" sizes="(min-width: 720px) 720px"><figcaption>gaps in communication</figcaption></figure><p>We often fill these gaps with assumptions. This might rarely be</p>]]></description><link>https://adnan-m.me/assumptions-in-communication/</link><guid isPermaLink="false">6418296461c8180573366f3f</guid><category><![CDATA[communication]]></category><category><![CDATA[teams]]></category><category><![CDATA[project management]]></category><category><![CDATA[soft skills]]></category><dc:creator><![CDATA[Adnan Mujkanovic]]></dc:creator><pubDate>Mon, 20 Mar 2023 10:16:03 GMT</pubDate><media:content url="https://adnan-m.me/content/images/2023/03/assumptions-in-communication.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: html--><div id="contents"></div><!--kg-card-end: html--><h2 id="the-problem">The Problem</h2><img src="https://adnan-m.me/content/images/2023/03/assumptions-in-communication.jpg" alt="Assumptions In Communication"><p>While communicating with other people, we might say a lot or hear a lot. Despite a large amount of available information during communication, a lot is still left out leaving gaps in the picture.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://adnan-m.me/content/images/2023/03/gaps-in-communication.jpg" class="kg-image" alt="Assumptions In Communication" loading="lazy" width="1280" height="300" srcset="https://adnan-m.me/content/images/size/w600/2023/03/gaps-in-communication.jpg 600w, https://adnan-m.me/content/images/size/w1000/2023/03/gaps-in-communication.jpg 1000w, https://adnan-m.me/content/images/2023/03/gaps-in-communication.jpg 1280w" sizes="(min-width: 720px) 720px"><figcaption>gaps in communication</figcaption></figure><p>We often fill these gaps with assumptions. This might rarely be a problem with people close to us that we spend a lot of time with but what about new people, groups, topics, projects, tasks etc. ? In these cases our assumptions might be off more often leading to <strong>misunderstandings</strong> and <strong>wasted time</strong>.</p><h2 id="the-solution">The Solution</h2><p>The solution for problems in communication is, who could&apos;ve guessed it, communication :D .</p><p>To avoid other people misunderstanding us, we need to <strong>continually</strong> practice and get better at our communication and better at ways of <strong>getting our point across</strong> as <strong>precisely and concisely</strong> as possible.</p><p>To avoid making <strong>wrong assumptions</strong> we have to <strong>continually</strong> practice <strong>careful listening</strong>, ponder about what is being said and while doing so <strong>ask questions</strong> or <strong>reiterate</strong> if in doubt or lacking understanding.</p>]]></content:encoded></item></channel></rss>