<?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"><channel><title><![CDATA[Gagandeep's Blog]]></title><description><![CDATA[Gagandeep's Blog]]></description><link>https://blog.gagan93.me</link><generator>RSS for Node</generator><lastBuildDate>Tue, 15 Sep 2026 21:22:46 GMT</lastBuildDate><atom:link href="https://blog.gagan93.me/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How Cursor boosted my productivity in 2025]]></title><description><![CDATA[Introduction
I've been working with Cursor for almost a year now. I'm a Senior Engineer at Branch International, where I've been for about 1.5 years. My job mostly involves updating old code or rewriting parts of the system, along with some projects ...]]></description><link>https://blog.gagan93.me/how-cursor-boosted-my-productivity-in-2025</link><guid isPermaLink="true">https://blog.gagan93.me/how-cursor-boosted-my-productivity-in-2025</guid><category><![CDATA[AI]]></category><category><![CDATA[Productivity]]></category><category><![CDATA[cursor]]></category><category><![CDATA[copilot]]></category><dc:creator><![CDATA[Gagandeep Singh]]></dc:creator><pubDate>Sun, 18 Jan 2026 10:55:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/wjFOjA2zXy8/upload/45b2dfe96cd23cd038dbe0c090e3110e.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>I've been working with Cursor for almost a year now. I'm a Senior Engineer at Branch International, where I've been for about 1.5 years. My job mostly involves updating old code or rewriting parts of the system, along with some projects for adding new features. Writing new code is often easier, but updating old code is harder — you have to make it better and easier to maintain without breaking anything that's already working. Before using Cursor, I used Sublime Text for more than 10 years and always preferred fast, simple editors over full-featured IDEs like IntelliJ. I mainly write backend code in Ruby with a bit of frontend work.</p>
<h2 id="heading-2025-was-significant">2025 was significant</h2>
<p>ChatGPT was launched in 2022, making AI accessible to the general public for the first time. Shortly thereafter, tools like GitHub Copilot emerged, leading to a rapid increase in adoption among developers. I personally utilized ChatGPT as a mock interviewer during my interview preparation in early 2024. Cursor was released in late 2024, and I began using it in December of that year. Transitioning to a new editor after more than a decade was challenging, but a few plugins allowed me to recreate my Sublime setup, including shortcuts, extensions, and themes. I have not looked back since. The year 2025 was also pivotal for Cursor as a company. A startup founded by first-time MIT entrepreneurs successfully captured over 50% of the market share, despite facing competition from Microsoft-backed GitHub Copilot and other similar tools.</p>
<p>On a personal note, my daughter was born in March 2025. This change initially threw off my work routine, but with some disciplined deep-work sessions and AI assistance, I managed to get back on track and even boost my productivity.</p>
<h1 id="heading-improved-productivity-with-ai">Improved Productivity with AI</h1>
<p>My role at Branch is filled with exciting opportunities to connect through meetings—stand-ups, 1:1s, unblocking sessions, interviews, and sometimes process-related discussions. To make the most of my time and keep meetings from taking over, I follow a few strategies:</p>
<ol>
<li><p>I start my day early, around 8-9 am (depending on how well the baby lets us sleep 😂). This allows me to carve out focus slots before meetings begin.</p>
</li>
<li><p>I push back or skip meetings where my presence isn’t crucial. Saving 30–60 minutes of uninterrupted time is more valuable than being passively present.</p>
</li>
<li><p>Weekly 1:1s without progress can become repetitive. Addressing this early frees up time for both parties to focus on what truly matters.</p>
</li>
<li><p>I try to schedule one interview per day. Design interviews already last more than an hour so it’s hard to take multiple interviews in a day.</p>
</li>
<li><p>I prioritize attending mandatory org-wide meetings and enjoy the flexibility of watching recordings for the rest.</p>
</li>
</ol>
<p>After all this, the time left for engineering tasks is limited, so I need to ensure I don't waste any of it. I was fortunate to read <a target="_blank" href="https://www.amazon.in/dp/0349413681?ref=ppx_yo2ov_dt_b_fed_asin_title">Deep Work</a> by Cal Newport a few years ago, which helped me apply some ideas to optimize my working style. I also wrote a <a target="_blank" href="https://blog.gagan93.me/habits-productivity-deep-work">detailed post</a> on this topic if you're interested in reading more). Since my role involves a lot of refactoring and rewriting, AI tools help me stay productive despite all these meetings.</p>
<h2 id="heading-ai-branch">AI @ Branch</h2>
<p>Before moving ahead, I’d like to explain how Branch views AI, as its usage becomes increasingly important when the organization encourages widespread adoption. In December 2024, I began using Cursor with a personal subscription. Some developers were already using GitHub Copilot or similar autocomplete tools, but none reported a significant productivity boost. Initially, I used Cursor for autocomplete, but after a few weeks, I started utilizing its agent mode, which allows interaction with AI to accomplish tasks. Having coded independently for over a decade, it took time to trust AI and grant it more control. As more developers in our organization began using Cursor, they reported increased productivity. This topic was discussed in our weekly engineering calls, leading to the creation of a working group to evaluate available tools for potential adoption by the entire team. Options like Copilot, Claude Code, and Cursor were considered, and the team began evaluating them.</p>
<p>The AI landscape is constantly evolving, making it challenging to conclude research. However, by mid-year, Branch had enterprise accounts for multiple AI tools, allowing developers to choose based on their preferences, IDE, and comfort level. Additionally, we hosted a hackathon where participants from outside the engineering team built projects without prior coding knowledge. This was just the beginning, and the organization is still working on optimizing workflows for all teams using the available tools.</p>
<h1 id="heading-my-cursor-usage">My Cursor Usage</h1>
<p>I started using Cursor like you all did - <em>tab tab tab</em> 🤣. These tools quickly grasp the context of your code. If you're still writing all the code yourself, you'll notice it peeking into your work and completing a line of code for you, suggesting the next line, or even the entire method. This is the most basic use of any AI-based editor.</p>
<p>Below, I'll talk about my setup, some tweaks, and my usage style that boosted my productivity in 2025.</p>
<h2 id="heading-understanding-different-models-cost-vs-quality">Understanding Different models - Cost vs. quality</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767524046204/f159f620-c5a4-46e2-8397-5432a4c036e5.png" alt class="image--center mx-auto" /></p>
<p>Just like other tools, Cursor supports multiple LLM models from different vendors ranging from OpenAI’s GPT models to Anthropic’s <em>Opus/Sonnet,</em> Google’s Gemini and their own recently launched model <em>Composer 1.</em> I won’t go into comparisons of these models but you must have a basic understanding of when to use different models. When choosing between different AI models, it's important to consider their strengths and limitations. Thinking models such as Opus and Sonnet are better suited for complex tasks due to their advanced reasoning capabilities and ability to handle nuanced queries. However, these models can be slower and may require more computational resources. On the other hand, models like Composer are designed for speed and efficiency, making them ideal for tasks that require quick responses but are not overly complex. Additionally, context windows play a crucial role in determining how much information a model can process at once. Larger context windows allow models to consider more information simultaneously, which is beneficial for understanding intricate problems. However, “larger context” also means slower processing times for a long running thread.</p>
<p>I've been using Cursor in <strong>auto</strong> mode for many months. It picks a model based on the task, but sometimes the responses were too slow, so I started trying different models. Here are a few reasons for the slowness:</p>
<ol>
<li><p>When the context window was about 80-90% full, it became large enough to slow down future conversations.</p>
</li>
<li><p>At around 98-99%, many conversations just stopped, and I had to start a new one, losing all the context 😔.</p>
</li>
<li><p>Sometimes, there was random slowness, which suggested their API might have been down or slow, like any other backend service.</p>
</li>
</ol>
<p>Thankfully, by September 2025 Cursor launched <strong>context summarisation</strong> that triggers automatically. You can also use <code>/summarize</code> command to trigger it on-demand.</p>
<p>Just like everyone else, I love coding quickly, so I tried out Cursor’s Composer model. I usually give smaller tasks with clear context to Cursor, and Composer-1 handled these simple tasks pretty well. I chatted with my colleagues about it, and they found it to be an average model for anything more complex. These days, I use a mix of Sonnet, Opus, and Composer for my projects.</p>
<h2 id="heading-configuring-how-agents-work">Configuring how agents work</h2>
<p>There are various ways in which you can provide a good <strong>initial context</strong> to the agent before it starts working on your task according to your prompt. As we’re using both Copilot and Cursor in our organisation, few folks from a working group have spent some time defining the configuration for both of these tools. Copilot searches for <code>.github/copilot-instructions.md</code> in your repository rules whereas Cursor has multiple ways of defining <a target="_blank" href="https://cursor.com/docs/context/rules">rules</a> and configuring the agent (eg. using <code>AGENTS.md</code>). When you are defining these configurations, consider including things like:</p>
<ol>
<li><p><strong>Project’s coding standards</strong>: Provide detailed guidelines for designing and organizing components within the codebase. This includes specifying folder structures, naming conventions, and rules for using components like models, services, and controllers. It ensures consistency and adherence to best practices across the project.</p>
</li>
<li><p><strong>Workflow Selection Criteria</strong>: Clearly define the conditions under which each workflow should be selected. This includes specifying when product, design, coding, testing, and review workflows are applicable. This ensures that the appropriate workflow is triggered based on the task requirements.</p>
</li>
<li><p><strong>Testing and Domain Specifications</strong>: Outline the testing plan, including the types of tests and their locations. This helps in maintaining a clear understanding of the project's structure and ensures comprehensive testing coverage.</p>
</li>
<li><p><strong>Error Handling and Logging</strong>: Define how errors should be handled within each workflow. Include guidelines for logging errors and exceptions to ensure that issues can be tracked and resolved efficiently.</p>
</li>
<li><p><strong>Security and Compliance</strong>: Outline any security protocols or compliance requirements that need to be adhered to during the development process. This could include data protection measures, access controls, and encryption standards.</p>
</li>
</ol>
<p>In brief, treat the agent as an integral part of your engineering team by providing it with a comprehensive understanding of the problem you're addressing. This approach ensures that each task the agent undertakes aligns with the expected quality standards. However, be mindful not to overload the initial context with excessive details, as this could consume a significant portion of the context window or lead to some instructions being overlooked. For example, if there are some very specific instructions, don’t include them in this configuration. You can mention those instructions or provide some files as a reference while solving that particular task.</p>
<h2 id="heading-planning-is-still-the-most-important-step">Planning is still the most important step</h2>
<p>A detailed task description is crucial for successful AI integration, as the quality of the output is directly related to the clarity of the input—essentially, <strong>garbage in, garbage out</strong>. By providing clear and comprehensive instructions, you enable AI to deliver more accurate and useful results.</p>
<p>I've written some internal posts at my organization on how to effectively break down and estimate tasks. Throughout my career, I have observed that many engineers face challenges with medium to large projects due to lack of proficiency in breaking them down into milestones and tasks. Projects often begin smoothly, but confusion tends to arise midway, leading to a rushed conclusion. This typically results in the submission of large pull requests with a significant <strong>blast radius</strong>.</p>
<p>With AI as your assistant, planning still remains one of the most important task in SDLC. Those who have reaped the most benefits from AI are typically individuals who excel at <strong>planning</strong> and <strong>breaking</strong> tasks. They understand that AI is not a magic wand that can replace the need for skilled software engineers. but a powerful tool that can enhance productivity when used correctly.</p>
<p>I'm not great at writing long prompts, so I've been handling my projects by breaking them down myself. I often have design discussions with AI when needed and then assign individual tasks to the tools to solve. This approach has worked well and quickly for me because:</p>
<ol>
<li><p>Not everything is outsourced to AI so I completely understand the problem statement. At times, it’s easy to lose context of the problem at hand by completely letting AI solve it.</p>
</li>
<li><p>Smaller problems are solved faster, so I can see the results immediately. I can then proceed to testing and raising smaller PRs for the individual tasks.</p>
</li>
<li><p>The code might be generated using AI but it will be committed against my name. In the future, if something breaks then the ownership is on me, not on AI. So it’s very important to completely go through the generated code. Even if the code is completely covered by test cases, I don’t get a feeling of satisfaction unless the I completely understand the generated and it looks maintainable enough. By working with smaller diffs, it’s easier to review the generated code completely before commiting</p>
</li>
</ol>
<p>Just like speed, I’m also a fan of <strong>smaller feedback loops</strong> so I wrote a <a target="_blank" href="https://gagan93.me/blog/2023/05/01/small-testing-loops.html">very short blog</a> on it years ago.</p>
<h2 id="heading-deeper-integrations">Deeper integrations</h2>
<p>Cursor also has integration with our issue tracker (Linear) and version control system (Github). By simply tagging <code>@cursor</code> on a ticket, we can launch a background agent that reads the requirement, understands it, and directly raises a pull request on Github with explanation of the changes. A developer can simply check the diff, see if it meets the requirements &amp; coding standards, and merge the PR 🚀.</p>
<p>Our role as engineers is evolving from primarily writing code to focusing on creating comprehensive and detailed tasks from Product Requirement Documents (PRDs) and design documents. This shift allows AI to handle these tasks efficiently and accurately.</p>
<h2 id="heading-understanding-legacy-code">Understanding legacy code</h2>
<p>Most of us join teams with existing codebases, often over a decade old, which can be difficult to understand at times. While those who witnessed the evolution of the system can grasp the reasons behind changes, newcomers may struggle to comprehend the current state. Code that spans multiple files or has high cyclomatic complexity is challenging to interpret.</p>
<p>AI tools are quite useful in these situations. I've been using AI extensively to understand and rewrite legacy systems. Both Cursor and Copilot have a read-only "Ask" mode which is particularly useful for learning and exploration, unlike the default "Agent" mode that is more suited for updating the code.</p>
<p>It’s hard for humans to unlearn — once a certain way of thinking or solving problems is ingrained, stepping back and approaching it with a completely fresh perspective takes conscious effort but AI can provide a fresh perspective on problems and suggest solutions as soon as you start a new chat window.</p>
<p>While detailed prompts are recommended to execute tasks as per your expectation, it has been beneficial for me to ask AI random questions like “<em>Why is this test case slow</em>". AI would then inspect the file, our test setup, and all the related files to give a fresh perspective into our setup. Without AI, you would generally treat most of the setup as source of truth. Other examples of random questions include "<em>How can this function be optimized</em>?" or "<em>What are potential security vulnerabilities in this code</em>?" These inquiries can reveal valuable insights and improvements. Additionally, AI can assist in design discussions, offering innovative ideas and solutions that might not be immediately apparent to human developers.</p>
<h2 id="heading-running-shell-commands">Running shell commands</h2>
<p>Often, the agent might need to run a script or execute a shell command. While it might seem risky to let AI access beyond the editor, there are safe ways to do it. Currently, both Cursor and Copilot can run shell commands from the editor’s AI agent and use the output to proceed.</p>
<p>These tools use an <strong>allow list</strong> to make this process quicker and safer. When an agent needs to run a shell command, it offers three choices: <strong>Skip</strong>, <strong>Run</strong>, and <strong>Add to allow list</strong>. Adding a command to the allow list means that next time, it can run without your permission. This feature is very helpful because I often found Cursor running read-only commands like <code>wc</code>, <code>awk</code>, <code>grep</code>, and <code>sort</code> safely, so it made sense to whitelist these commands. Without this feature, you would have to constantly approve commands in your editor.</p>
<p>It's not safe to whitelist commands like <code>rm</code>, but since I frequently commit my code, I've whitelisted them too. Because Cursor doesn't run with sudo privileges, this setup is both safe and efficient. For the past two months, I've been working on projects that required rewriting the controller layer for many modules. One of my testing goals was to ensure that the JSON output from the old and new controllers matched. If there were differences, I needed to know exactly what changed. I used AI-written scripts to compare each nested key in the JSONs to avoid regression issues. Directly asking AI agents to compare the JSONs could work but might be less precise, so I had it write a script for this task.</p>
<h2 id="heading-for-writing-tests">For writing tests</h2>
<p>Tests are essential for identifying regressions and instilling confidence in the codebase over time. They ensure that new changes do not disrupt existing functionality and help maintain the software's integrity. Many developers now use AI to write tests, including those who previously avoided writing them. I have long advocated for writing tests, even before the widespread adoption of AI agents. There are two main approaches to writing tests: one targets achieving 100% code coverage, while the other focuses on covering meaningful scenarios. I will outline my approach to writing tests:</p>
<ol>
<li><p>I'm not into Test-Driven Development (TDD), but I ensure that I write tests for most of the features I touch.</p>
</li>
<li><p>As the code is already written and I maintain a clean coding style, I can easily identify which code paths need testing.</p>
</li>
<li><p>I determine the types of tests required, which generally include unit tests (for individual classes), controller tests (to test a controller's journey), and end-to-end tests (covering complete scenarios involving multiple controller invocations).</p>
</li>
<li><p>To avoid reviewing subpar AI-generated code, I provide an initial structure for the AI to follow, allowing it to handle the repetitive tasks.</p>
</li>
</ol>
<p>A sample in Ruby might look like this:</p>
<pre><code class="lang-ruby">RSpec.describe &lt;file&gt; <span class="hljs-keyword">do</span>
  <span class="hljs-comment"># leave setup data for AI</span>

  describe <span class="hljs-string">'#method_to_test1'</span> <span class="hljs-keyword">do</span>
    context <span class="hljs-string">'when scenario 1'</span> <span class="hljs-keyword">do</span> 
      <span class="hljs-comment"># leave this for AI to fill</span>
    <span class="hljs-keyword">end</span>

    context <span class="hljs-string">'when scenario 2'</span> <span class="hljs-keyword">do</span> 
      <span class="hljs-comment"># leave this for AI to fill</span>
    <span class="hljs-keyword">end</span>
   ...
  <span class="hljs-keyword">end</span>

  describe <span class="hljs-string">'#method_to_test2'</span> <span class="hljs-keyword">do</span>
    context <span class="hljs-string">'when scenario 1'</span> <span class="hljs-keyword">do</span> 
      <span class="hljs-comment"># leave this for AI to fill</span>
    <span class="hljs-keyword">end</span>

    context <span class="hljs-string">'when scenario 2'</span> <span class="hljs-keyword">do</span> 
      <span class="hljs-comment"># leave this for AI to fill</span>
    <span class="hljs-keyword">end</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>I've been encouraging my juniors to write tests this way (since before the AI era) — first list the scenarios, then dive into the details. Doing it this way ensures you think through the scenarios carefully without getting into the specifics of setup code or mocks. If you start writing tests for one scenario without defining all of them, you might have to switch your focus between thinking about cases and writing the actual test cases. Fortunately, the grunt work can now be outsourced 😎.</p>
<p>I’ve also tried without giving AI the structure and I didn’t get good output. But it was some time ago, maybe the models have improved now. Or a better idea would be to frame the prompt like this:</p>
<blockquote>
<p>Write tests cases for @this_file. Refer @that_file for understanding how to organise the scenarios.</p>
</blockquote>
<h2 id="heading-mcp-servers">MCP servers</h2>
<p>If you have not yet explored MCP servers, you’re missing out on some serious automation. Most of the popular websites have rolled out their MCP servers and people are already building agents over them 🚀. While I’ve not developed any full fledged agentic workflows yet, these are the things that I’ve used MCP for:</p>
<ol>
<li><p><strong>Debugging high impact production issue</strong> - Our major backend web application is a monolith that’s deployed twice a week. Every month, I get to handle my team’s on call for a week. In August 2025, I got an issue assigned that was causing problem in a specific module which was essential for all markets, so it had a major impact. Seeing the charts, it was clear that something started breaking after the most recent deployment. While rolling back the entire release was an option, it is not always the preferred path because a release contains work of multiple developers and unless it’s a SEV0, you would not want to revert everyone’s work. But I was not able to catch the issue by manually looking into the code as there were no recent changes around the impacted code or it’s parent classes. I had recently integrated Github MCP with Cursor, and I thought of debugging this issue with MCP integration. Because each release going to production has a PR, I gave the PR link and the exception trace to Cursor and asked it to debug the same using Github MCP tools. Within a minute, it was able to point out that a framework upgrade has also went live within the same release and some methods we used in our module could have been impacted in the newer version. It gave me a direction to look into and that was the exact issue which I fixed later and released within next one hour.</p>
</li>
<li><p><strong>Filing &gt;100 tickets</strong> - We use Linear as our issue tracker and I had a use case of filing many linear tickets with a given title and details. Apart from this, the tickets had to be assigned to people from multiple teams so I had to ensure that the project is linked to their team’s board. I spent half hour with Cursor and I was able to file 100+ tickets with accurate details and team information. In a pre-AI world, this would anyways be done with a script but the effort of writing that by hand got eliminated.</p>
</li>
<li><p><strong>Frontend tasks</strong> - I recently got assigned some UI work for an urgent task. While I’ve been a full-stack engineer but my inclination has been towards backend only. I googled and found out that our design platform (Figma) also has a MCP server that can be connected to AI editors. Within minutes, I could access designs from my editor and give commands to build specific parts of the page. This entirely eliminated the mental effort in building UI structure by hand.</p>
</li>
</ol>
<h2 id="heading-avoiding-ai-slop">Avoiding AI Slop</h2>
<blockquote>
<p>AI slop code refers to low-quality, often buggy, insecure, or nonsensical code generated rapidly by AI tools, lacking true value or originality, and is a significant concern as it can introduce errors like missed security checks, hallucinated functions, and inefficiency, but can be avoided by experienced developers using AI as a supplement, not a replacement, and always validating the output.</p>
</blockquote>
<p>It’s very common to get excited with everything happening in the AI world and put it in the driver’s seat, but that can quickly backfire. AI models work by predicting the next token based on patterns learned from large datasets. They don’t truly understand your system, its constraints, or its long-term trade-offs. Because of this, the output may look correct and even pass tests, yet still be suboptimal, brittle, or misaligned with your architecture. Your organisation has hired you, not an AI agent, so it’s your responsibility to ensure that while AI output helps you move faster, it doesn’t quietly introduce slop into the codebase.</p>
<p>Few months ago, when more people in our team started using AI tools, I got to see a lot of obvious comments after every few lines in their pull requests. If you’ve been a Ruby developer, you know that the <a target="_blank" href="https://github.com/rubocop/ruby-style-guide?tab=readme-ov-file#comments">style guide</a> says this about comments:</p>
<blockquote>
<p>Good code is its own best documentation. As you’re about to add a comment, ask yourself, "How can I improve the code so that this comment isn’t needed?". Improve the code and then document it to make it even clearer.</p>
</blockquote>
<p>Having coding in Ruby for more than a decade, I generally write close to zero comments in my code (feel free to call me extremist here 😂). There are very rare cases sometimes when you see a specific complexity in the codebase that would take time to refactor so in those cases I leave a useful comment. This is one example of AI slop but there can be many more. For example, if you’re designing web pages using AI and you don’t give them sufficient context of your overall application and design scheme, the AI agent would end up redefining a lot of unneeded CSS inline just to match the design.</p>
<p>In my knowledge, there are two easy ways to avoid such slop:</p>
<ol>
<li><p>Define the <code>Agent.md</code>, <code>copilot-instructions.md</code> or similar files to setup top level behaviour for the agent.</p>
</li>
<li><p>Ensure your prompts are clear enough so that no slop is generated.</p>
</li>
</ol>
<p>Even then if you get any such sloppy code, it’s your responsibility to clean that up manually before committing.</p>
<h1 id="heading-is-ai-eliminating-software-engineers">Is AI eliminating software engineers?</h1>
<p>From my personal experience, and from listening to respected industry voices, I’ve concluded that AI today can help you build apps from scratch and even get some paying users. But building serious applications is still fundamentally a system design problem that needs experienced engineers. You need a simpler stack, fewer proxies, fewer network hops, and carefully optimised code to run systems efficiently at scale.</p>
<p>Also, in the realm of serious software development, writing code is just one part of the job. A significant amount of effort goes into prioritizing tasks, deciding on product behavior, managing releases, providing post-release support, ensuring observability, debugging through logs and metrics, and handling incidents. Automating parts of code writing, even though they still need testing, reviewing, merging, and releasing, optimizes about 15-20% of the overall process.</p>
<p>Still AI saves a lot of valuable time for the engineers and significantly reduces costs, which is truly impressive. So if you ask me: <em>Do we still need developers</em>?. I’d say <strong>absolutely</strong>. We probably need fewer of them.</p>
<h2 id="heading-two-edged-sword">Two edged sword</h2>
<p>AI is undeniably powerful and already indispensable. The challenge is to use it as a leverage tool, not as a crutch, so that we don’t trade long-term engineering strength for short-term speed. While I regularly use AI in my work, I see a few long-term issues that are worth talking about:</p>
<ol>
<li><p><strong>Today vs 5-10 Years Ago</strong> - I started coding professionally in 2015, when StackOverflow was the OG. Until around 2022, someone who coded fast was usually someone who had coded for years and had built both speed and expertise by writing code for a long time. That relationship has changed. AI now makes everyone a designer, a poet, and a coder. The problem is not capability, it’s learning. A large part of the learning is shifting from humans to machines. For example, I’m fluent in Ruby and reasonably comfortable with Java and JavaScript. If you ask me to write C or C++, I’ll still open an editor like Vim and rely on what I learned in college 12–13 years ago. But if you ask me to write Go or Rust today, I’ll most likely describe the idea to an AI editor. I’ll ship a “hello world” in seconds and probably an MVC app in days, but I may never become truly comfortable with those languages without AI. Human brains get comfortable with syntax by writing it again and again. Outsourcing that repetition to AI speeds up delivery, but it slows down internalisation. While writing this, I also remembered my first company’s interview process, where I wrote C++ code with pen and paper 🤠. Will people ever do that again? Maybe I’m overthinking or sounding old, but the idea is simple: programming languages are slowly becoming interfaces for people who don’t deeply understand their syntax, treating engineers and non-engineers alike. The upside is that new developers will ship and debug production code much faster than we ever did. The downside is that it may take them much longer to become fluent in any one language. Whether this is good or bad — only time will tell.</p>
</li>
<li><p><strong>Long-Term Maintainability</strong> - A big worry is "AI slop." Code gets approved not because it's well understood, but because it works and passes tests. Developers might accept suggestions they don't fully understand, especially when rushed. Over time, this results in codebases that are harder to understand, harder to change, and fragile in unexpected ways.</p>
</li>
<li><p><strong>Skill Atrophy and Debugging Depth</strong> - When AI writes most of the scaffolding and glue code, developers spend less time building a mental model of the system. This can show up during incidents. Debugging distributed systems, performance issues, or subtle data bugs still requires deep understanding, not just good prompts. If that understanding erodes, incident recovery times can increase, even if development felt faster initially.</p>
</li>
<li><p><strong>Lock-In and Rising Costs</strong> - Currently, AI tools are relatively affordable due to growing adoption and high competition. However, as teams and individuals become increasingly dependent on these tools, pricing power will shift. When these tools become essential for development, costs are likely to rise. At that point, opting out will be mentally challenging. While the costs might still be more justifiable than hiring additional engineers, only time will reveal how cost-effective these tools will remain.</p>
</li>
</ol>
<h1 id="heading-personal-targets-for-2026">Personal targets for 2026</h1>
<p>With 2025 bringing so many improvements in these AI models, I’m excited to see what we have for 2026 and beyond. For this year, I’m looking to try out a few things:</p>
<ol>
<li><p><strong>Agentic workflows</strong> -Automation has been around in software engineering for a long time, but creating it has become easier with Agentic workflows. I explored tools like n8n in late 2025 when my manager made a small agent to set up a daily alert on some reports. Building agentic workflows on these platforms is like drawing diagrams. It's easier because of the visuals, and it lowers costs by cutting out</p>
<p> the need for software engineers. While the tool is very powerful, I didn't have any ideas to implement, so I didn't create any agentic workflows last year. I'm usually not great with ideas, but I'm hopeful that this year I'll develop an agentic workflow for personal use or a production application. Let's see if I can achieve this 😎.</p>
</li>
<li><p><strong>Cursor commands</strong> (<a target="_blank" href="https://cursor.com/docs/agent/chat/commands">more</a>) - I’ve heavily used cursor this year and often searched for old chats when I had to perform similar tasks so that I’m not required to provide the same prompt again. While this is doable and context window isn’t a problem after <a target="_blank" href="https://cursor.com/docs/agent/chat/summarization#how-summarization-works">context summarisation</a> feature, a better method would be to create reusable prompts and commit them to the respective repo. I’ll try to find such use cases and create their cursor commands to help both myself and the wider team.</p>
</li>
<li><p><strong>Agent skills</strong> (<a target="_blank" href="https://code.claude.com/docs/en/skills">more</a>) - I got to hear about this very recently. The idea is that agent can pick a skill to perform a specific task and using a markdown file, it would know how to do that task in your preferred way. I believe this would mean that you no longer need long, carefully crafted prompts or repeated context-setting every time you need to perform these tasks since the agent already knows how to perform the task, what steps to follow, and what constraints to respect.</p>
</li>
<li><p><strong>End to end execution with AI</strong> - For small projects, I generally start implementing my tasks without creating detailed technical tasks from the PRD. Even for technical projects, I’ve seen that my project description is detailed but the individual tasks have minimal information. Recently, some folks in my organisation are exploring AI for end to end execution. The flow looks like:</p>
<ol>
<li><p>Create a design doc from PRD (manually or with AI’s help).</p>
</li>
<li><p>Use AI to create project, tasks and milestones from design doc. These tasks would include required details with code snippets, or any other thing that would help the agent understand the context deeply.</p>
</li>
<li><p>Run agents (sequentially or parallelly) to execute each task and monitor the generated code.</p>
</li>
</ol>
</li>
<li><p><strong>Parallel agents / subagents</strong> - Till now, my way of using AI has been to give it a task and stay glued to the editor screen because the tasks are small enough and agent respond mostly within a minute. But the real power lies in giving them different tasks simultaneously to improve efficiency.</p>
</li>
</ol>
<h1 id="heading-conclusion">Conclusion</h1>
<p>As the AI landscape has evolved in 2025, it feels increasingly realistic to pursue these goals and even beyond. The steady pace of improvement in AI tools opens up new possibilities for efficiency and experimentation, but it also comes with trade-offs that require thoughtful adoption rather than blind optimism. With a focus on learning, adapting, and using these tools deliberately within real-world constraints, I see room to make meaningful progress without losing sight of engineering fundamentals.</p>
<p>I’d genuinely like to learn from others here. So if you’ve developed useful workflows, or discovered patterns that work well in real production environments, please share them in comments.</p>
]]></content:encoded></item><item><title><![CDATA[Redis Production Insights]]></title><description><![CDATA[Background
I began my first full-time role in June 2015. Since then, two technologies have consistently been part of my journey: Rails and Redis. Every company and project I have been involved with has utilized these technologies alongside others. In...]]></description><link>https://blog.gagan93.me/redis-production-insights</link><guid isPermaLink="true">https://blog.gagan93.me/redis-production-insights</guid><category><![CDATA[Redis]]></category><category><![CDATA[caching]]></category><category><![CDATA[Databases]]></category><category><![CDATA[production]]></category><dc:creator><![CDATA[Gagandeep Singh]]></dc:creator><pubDate>Fri, 17 Oct 2025 04:53:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/JKUTrJ4vK00/upload/fcbd8f3cfc226e06fe44315b1364f33c.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-background">Background</h1>
<p>I began my first full-time role in June 2015. Since then, two technologies have consistently been part of my journey: Rails and Redis. Every company and project I have been involved with has utilized these technologies alongside others. In November 2015, <a target="_blank" href="https://github.com/antirez"><strong>Antirez</strong></a> (the author of Redis) wrote a blog post revealing a <a target="_blank" href="https://antirez.com/news/96"><strong>security vulnerability</strong></a> in Redis. This vulnerability allowed hackers to gain access to the machine where Redis was running through SSH if the Redis server was operating in non-protected mode (which was the default at that time) and the bind address was set to <code>0.0.0.0</code>. The <a target="_blank" href="https://en.wikipedia.org/wiki/CopperEgg"><strong>project</strong></a> I worked on was a website and host monitoring SaaS tool developed in 2010. At that time, creating a VPC was not mandatory, and all machines could run in a single flat network hierarchy called EC2-Classic (which AWS <a target="_blank" href="https://www.allthingsdistributed.com/2023/09/farewell-ec2-classic.html"><strong>recently retired</strong></a>). This project also provided me the opportunity to move the entire system (from app servers to databases) inside the VPC by relaunching them gradually. The concepts I learned about VPC, networking, and security 8-9 years ago remain relevant today 😁.</p>
<p>Returning to the vulnerability — because we had a monitoring product that checked website uptime from different regions worldwide (similar to <a target="_blank" href="https://uptimerobot.com/"><strong>this</strong>)</a>, we had our website probing service running in various AWS regions and even on smaller cloud providers like <a target="_blank" href="https://www.linode.com/"><strong>Linode</strong></a> (Lin<a target="_blank" href="https://www.linode.com/">o</a>de was a very simple and tiny cloud provider then). The application we ran was a small Ruby service with an attached Redis, deployed across multiple regions and providers. Back then, Docker and Kubernetes were not mainstream, and server setup was a mix of bash scripts and manual interventions. While our publicly exposed servers had basic SSH key authentication and services like <a target="_blank" href="https://github.com/fail2ban/fail2ban"><strong>fail2ban</strong></a> to handle repeat offenders, Redis was mostly running on default configuration, with the server bound to <code>0.0.0.0</code>. This meant that someone could connect to Redis from outside if port 6379 (the default Redis port) was not protected through a firewall or security groups. As I mentioned, we were running this service on multiple cloud providers, which were not as feature-rich as AWS.</p>
<p>Many of our VMs running Redis were hacked, and this issue <a target="_blank" href="https://news.ycombinator.com/item?id=10537852"><strong>impacted</strong></a> numerous internet businesses in the following days. Fortunately, we resolved the issue by binding the machine to the localhost address (<code>127.0.0.1</code>), and a future upgrade of Redis enabled protected-mode by <a target="_blank" href="https://www.reddit.com/r/redis/comments/3zv85m/new_security_feature_redis_protected_mode/"><strong>default</strong></a>. This was my first production encounter with a security breach that directly impacted our business because we lost access to those servers, and the feature stopped working. This taught me an important lesson - <strong>while default settings are acceptable for experimentation, they might not be suitable for running a production application.</strong></p>
<p>In this blog post, I want to share my experience running Redis in production over the years. Although I haven't managed a very complex Redis infrastructure, these experiences should still benefit those who operate databases or are generally interested in learning about them.</p>
<h1 id="heading-what-is-redis">What is Redis?</h1>
<p>Before using Redis, I was only familiar with SQL databases, so Redis seemed very different. I referred to it as a “data-structures db” because it implemented many commonly used data structures like lists, hashes, sets, etc. In all the codebases I’ve reviewed, the most common use case for Redis is as a key-value store (essentially string-based <a target="_blank" href="https://redis.io/docs/latest/commands/get/"><strong>GET</strong></a>, <a target="_blank" href="https://redis.io/docs/latest/commands/set/"><strong>SET</strong></a>). However, Redis supports many more features beyond this:</p>
<ol>
<li><p>SET allows the EX parameter to set expiring keys, a very useful feature for cache invalidation.</p>
</li>
<li><p>SET also allows the NX parameter to set the key only if it doesn’t exist. SET with the XX parameter does the opposite. At the application layer, libraries return a boolean value to indicate if the key was set or not. This is very useful for implementing distributed locks. In fact, Redis is very popular for implementing <a target="_blank" href="https://redis.io/docs/latest/develop/clients/patterns/distributed-locks/"><strong>distributed locking</strong></a>.</p>
</li>
<li><p>The <a target="_blank" href="https://redis.io/docs/latest/commands/?group=list"><strong>List</strong></a> data structure in Redis allows pushing and popping from both the left and right sides, making it useful as both a stack and a queue. In fact, the most popular background job libraries for <a target="_blank" href="https://github.com/celery/celery"><strong>Python</strong></a> and <a target="_blank" href="https://github.com/sidekiq/sidekiq"><strong>Rub</strong></a><strong>y</strong> use Redis lists for queuing.</p>
</li>
<li><p>Redis also supports Geospatial calculations. A use case for this could be storing map locations of multiple fuel stations in your area and then using Redis commands to determine which one is closest to your location. Or perhaps list all the fuel stations in ascending order of their distance, within 3 km. Isn’t that cool?</p>
</li>
<li><p>Redis also supports <a target="_blank" href="https://redis.io/docs/latest/commands/?group=pubsub">PubSub</a> and <a target="_blank" href="https://redis.io/docs/latest/commands/?group=stream">Streaming</a>. PubSub enables real-time, fire-and-forget message broadcasting, making it perfect for instant updates like chat messages or live stock prices where no history is needed. In contrast, Streams provide a durable, replayable event log with consumer groups, ideal for processing workflows or pipelines that need message persistence and ordered delivery.</p>
</li>
<li><p>Redis also supports approximate data structures like <a target="_blank" href="https://redis.io/docs/latest/commands/?group=bf">Bloom filters</a>, <a target="_blank" href="https://redis.io/docs/latest/commands/?group=hyperloglog">HyperLogLogs</a> and has support even for <a target="_blank" href="https://redis.io/docs/latest/commands/?group=timeseries">TimeSeries</a> data.</p>
</li>
</ol>
<p>While the most common usages are for String, List, and Hash data structures, Redis supports a wide range of other features out of the box. If you’re new to Redis or not familiar with these features, I recommend trying them on your local machine.</p>
<h2 id="heading-use-cases-solved">Use cases solved</h2>
<p>At it’s core, Redis is a NoSQL database that supports lots of data structures and also solves for many other use cases like streaming, time series data sampling, geospatial computation, etc. In this section, we’ll explore more about Redis use cases for providing standard solutions to common problems:</p>
<ol>
<li><p><strong>Caching</strong> - If you’re using Redis in production, there’s a high likelihood you’re using it for caching. Developers love Redis for caching due to its in-memory nature, which helps improve service response times and reduce load on SQL databases.</p>
</li>
<li><p><strong>Queueing</strong> - As previously explained, Redis is widely used as a queue to solve the problem of distributed background processing. You don’t need to write anything beyond business logic, as the problem is common and already solved by libraries in your preferred language using Redis as a queue.</p>
</li>
<li><p><strong>Rate limiting</strong> - Building a Rate Limiter is a common problem, especially when exposing APIs to the public. There are multiple algorithms to implement a Rate Limiter that can be built over Redis using its supported data structures.</p>
</li>
<li><p><strong>Distributed locking</strong> - If you’re from a SQL background, you might have used Row locks (like <code>SELECT FOR UPDATE</code> in <a target="_blank" href="https://www.postgresql.org/docs/current/sql-select.html">Postgres</a>) for similar purposes. However, many times you’ll have use cases to lock something not yet persisted in a SQL database. Redis locks are handy in those cases.</p>
</li>
</ol>
<p>Enough of selling Redis, let’s discuss some learnings.</p>
<h1 id="heading-learnings-and-recommendations">Learnings and Recommendations</h1>
<h3 id="heading-do-i-need-the-multiple-redices">Do I need the multiple Redices?</h3>
<p>I’ve seen applications grow from a few thousand users to millions. In a simple stack, we usually start with one Redis instance. As the product grows, more developers realize use cases for Redis and continue building on top of it. However, there are instances when you should consider adding a second Redis machine to your infrastructure:</p>
<ol>
<li><p><strong>Sharing across services</strong> - As with any other database, we should avoid sharing a Redis machine across different services. A Redis instance shouldn’t be considered as a dump-all cache for multiple services. The primary reason for this is the single point of failure, and no one would want that kind of setup.</p>
</li>
<li><p><strong>Having different use cases</strong> - In a previous organization, we had an application with four Redis instances connected to it. Each had a different use case and its own configuration. Separating databases based on use cases helps in tuning and managing them separately. Some people might not do this initially and later realize that separating them initially was a good idea.</p>
</li>
<li><p><strong>Separating cache vs. persisted storage</strong> - This is a special case of previous point (different use cases). Sometimes it’s still ok to not separate Redices for use cases because they’re small enough but it’s never okay to combine use case of <em>cache</em> vs <em>persisted storage.</em> Most of the people put Redis in a place where <a target="_blank" href="https://redis.io/docs/latest/commands/flushall/">flushing</a> it doesn’t impact anything (apart from some intermittent CPU peaks in the primary database) because the data can be repopulated. But there are some use cases where you can’t afford to drop database because Redis is the primary database. One very good example for this is when you use Redis as a database for background job processing (<a target="_blank" href="https://github.com/celery/celery">example1</a>, <a target="_blank" href="https://github.com/sidekiq/sidekiq">example2</a>). In such cases, you cannot afford to lose jobs even if the Redis process crashes, making it important to run Redis in persisted mode. Depending on your Redis version and current configuration, you might need to tune it for your persistence use case, balancing durability and performance.</p>
</li>
</ol>
<p>Based on use cases, persistence, latency, and other tuning requirements, you might want to separate Redis early on to avoid migration overhead.</p>
<h3 id="heading-using-expiring-keys">Using expiring keys</h3>
<p>I’ve seen apps where the count of keys easily grows beyond 10 million. It’s not bad to have this many keys as long as you have the memory available, but in some cases, it might not be justified given the app size and use cases. A primary reason for <em>ever-growing keys</em> in Redis is <strong>not setting expiry</strong> for keys. On analyzing key patterns on various Redis machines, I’ve often come across keys for which the corresponding application code was deleted long ago, but the developers didn’t clean up the respective keys. As Redis is an in-memory database, useless keys mean wasted RAM. I’m not saying all keys should be expiring, but if you’re considering running in cache mode, your application code should manage repopulating Redis as needed. Some good examples of expiring keys are:</p>
<ol>
<li><p>When Redis is used for distributed locking, we can set an expiry on the lock key. This TTL is generally higher (or a few times) than the task execution time. This ensures that if the application logic fails to clean up the lock key, it gets automatically cleaned up, avoiding infinite locking.</p>
</li>
<li><p>We often use Redis for storing temporary passwords and OTPs, and in most product specifications, there is an expiry associated with this OTP. Setting TTL on such keys helps build this feature without additional application logic.</p>
</li>
</ol>
<p>If you see your Redis memory graph in an ‘ever-increasing trend”, it might be a good time to revisit if you really need all those keys 😁.</p>
<h3 id="heading-caching-invalidation-amp-application-hooks">Caching invalidation &amp; application hooks</h3>
<blockquote>
<p>There are only two hard things in Computer Science: cache invalidation and naming things.</p>
<p>-- Phil Karlton</p>
</blockquote>
<p>Although this is not directly related to Redis, I often see the problem of cache invalidation discussed among people who use Redis for caching. So I thought of sharing a few best practices. Caching is a powerful technique for improving application performance, but it comes with the challenge of cache invalidation. Properly managing cache invalidation is crucial to ensure your application serves accurate data. Here are some key points to consider when setting up cache invalidation hooks:</p>
<ol>
<li><p><strong>Understand the Data Lifecycle</strong>: Before setting up cache invalidation, it's important to understand the lifecycle of the data being cached. Identify when data changes and how those changes should be reflected in the cache. I’ve often seen people caching the response of an entire API to save time in querying and serialization. This is good if the API is Restful, but if it queries multiple data sources to generate that response, there might be multiple hooks on which you need to reset the cache. So take care of such edge cases.</p>
</li>
<li><p><strong>Suitable Hooks for Invalidation</strong>: We should update the cache once the data in primary data source has been updated. For a Rails application, we use <code>after_commit</code> hook for the respective model to update the respective cache. I asked ChatGPT for parallels in other languages/frameworks and got this for your reference 😁</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1760185846164/adc64276-9ef5-4c17-946e-c79d167e4241.png" alt class="image--center mx-auto" /></p>
</li>
<li><p><strong>Set Expiry Times</strong>: ome people use expiry times as a band-aid for cache invalidation. This could be to defer the fix for the actual bug in some cases (e.g., when you don’t know all the <em>hooks</em> to invalidate the cache). A similar case where we used such expiring keys in the past was to update feature rollouts for a user. To see enabled features for a user, we earlier relied on a SQL-backed system that performed checks on at least four levels before telling if the feature is enabled on the user or not. But as the system grew in complexity and we added an authorization layer, such checks became more widespread. The query to check enabled features got flagged as one of the most used queries in our <a target="_blank" href="https://en.wikipedia.org/wiki/Application_performance_management"><strong>APM tool</strong></a>, and we fixed this in two phases: In the first phase, we cached this data for 5 minutes and ensured that we don’t query from the SQL database within these 5 minutes. While this might not be acceptable in many use cases, it was okay for this use case as the latency of 5 minutes for turning a feature on/off was acceptable to product and business teams. As we got some breathing space, we figured out all the hooks in the different models to invalidate this cache. We could have completely removed the TTL, but we rather set it to a higher value (e.g., 7 days) to ensure that we don’t cache data for users who don’t come to our app often.</p>
</li>
<li><p><strong>Use Versioning</strong>: Implementing versioning for cache keys can help manage cache invalidation. By appending a version number to cache keys, you can easily invalidate old entries by incrementing the version number when data changes. For systems where we have timestamps like <code>updated_at</code>, developers often use that as a versioning key because it naturally tracks an <em>update</em> in the system.</p>
</li>
</ol>
<p>By carefully setting up cache invalidation hooks and strategies, you can maintain the integrity of your application's data while leveraging the performance benefits of caching.</p>
<h3 id="heading-tuning-the-configuration">Tuning the configuration</h3>
<p>This is true for all databases, not just Redis. Whenever you’re planning to run anything in production, it’s good to know about various parameters that you can tune. For ages, we’ve been operating single-node centralized databases that persist data on disk, and they generally run fine with default settings unless you hit some metric (IO/CPU/RAM) really hard. But the same is not true for distributed NoSQL databases. Based on the configuration, such databases can be tuned to compromise consistency or availability for speed, scalability, and fault tolerance.</p>
<p>All databases have at least one configuration file available to tune different parameters for your use case. There are sufficient comments in the file for documentation purposes (although their websites have more detailed explanations, if needed). You can access these files to tune the database only if you’re self-managing the database installation on a virtual machine like AWS EC2. If you’re using managed services like AWS RDS, AWS Elasticache, etc., then you’d be exposed to a limited set of parameters through their GUI (like <a target="_blank" href="https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_WorkingWithParamGroups.html"><strong>Parameter Groups</strong></a> in AWS).</p>
<p>To see a sample Redis conf, you can check <a target="_blank" href="https://download.redis.io/redis-stable/redis.conf">this link</a> or google “<em>redis stable conf”</em> . I’ll talk about a few useful config parameters for Redis:</p>
<ol>
<li><p><strong>Persistence</strong>: Redis, by design, is an in-memory data store, which makes it extremely fast but raises an important question — <em>what happens to your data when the process crashes or the machine restarts?</em> To handle this, Redis supports multiple persistence mechanisms that balance speed, durability, and operational overhead differently. Broadly, Redis offers two major forms of persistence: RDB (snapshotting) a<a target="_blank" href="https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_WorkingWithParamGroups.html">nd AOF (append-o</a>nly file).<br /> The <strong>RDB</strong> persistence mechanism takes <strong>point-in-time snapshots</strong> of your dataset and writes them to disk as a <code>.rdb</code> file. Redis forks a background process (also called as BGSAVE or background save) to perform the dump, ensuring the main process remains responsive. It reads your <code>save</code> configuration from the <code>redis.conf</code> to decide when it should run the background save process. I’ll explain this with an example of <code>save</code> parameter.</p>
<pre><code class="lang-bash"> save 900 1
 save 300 10
 save 60 10000
</code></pre>
<p> This configuration means: save after <strong>1 change</strong> within <strong>15 minutes</strong>, or save after <strong>10 changes</strong> within <strong>5 minutes</strong>, or save after <strong>10,000 changes</strong> within <strong>60 seconds</strong>. These rules can be customized or even disabled with <code>save ""</code> for a super-fast cache-like setup.<br /> The other setup is AOF where Redis provides finer-grained durability than RDB by logging <strong>every write operation</strong> Redis performs. On restart, Redis replays the AOF log to rebuild the in-memory dataset. The two important parameters here are: <code>appendonly</code> (should be set to <code>yes</code> to use this mode) and <code>appendfsync</code> . The second property defines how frequently you flush to the disk (possible values: <code>no</code>, <code>everysec</code>, <code>always</code>). In most production environments, <code>appendonly everysec</code> is a safe balance — ensuring sub-second durability without blocking throughput. For extremely fast ephemeral caches, <code>appendonly no</code> can be used for maximum performance. If you set this property to <code>always</code>, Redis will enter into <strong>fully durable</strong> mode. This means — every time Redis executes a write command (e.g. <code>SET</code>, <code>LPUSH</code>, <code>INCR</code>), it <strong>appends that command</strong> to the <strong>Append-Only File (AOF)</strong> on disk, and then calls the <code>fsync()</code> system call immediately afterward — before acknowledging success to the client. This guarantees that even if the OS or machine crashes immediately after acknowledgment, the data is physically on disk and recoverable.</p>
<p> <strong>Note on fsync</strong>: <code>fsync</code> is a system call that ensures all pending writes for a given file descriptor are flushed from the kernel’s page cache to the physical storage device. By default, when you call <code>write()</code>, the data goes into the <strong>kernel’s buffer cache</strong>, not immediately to disk — the OS decides when to flush it. <code>fsync()</code> forces that flush <strong>right now</strong> and waits until the device confirms that data is persisted.</p>
</li>
<li><p><strong>Databses</strong> - There’s a controversial feature inside Redis. Redis by default supports 16 databases (numbered 0 to 15) which we can choose using the SELECT command. I’ve not used this in many projects but in of the use-cases, this feature helped me separate data into different namespaces. I was talking to someone few months ago and they mentioned that this feature is considered as an anti-pattern. To understand a bit more, I did some research and found why some people don’t like this feature:</p>
<ul>
<li><p>Redis cluster mode doesn’t support databases because of implementation complexity. So it’s supported only if you’re running a single node Redis (with/without replicas).</p>
</li>
<li><p>All DBs share same memory, persistence, and config. So there’s no isolation in the way data is stored in memory or on disk. This also means that the configuration we chose for the server applies to all the databases.</p>
</li>
<li><p>There are commands like <code>FLUSHDB</code> to flush one database (the one you’re currently on), but there’s also a <code>FLUSHALL</code> command that wipes all databases, easy to cause total data loss by mistake.</p>
</li>
<li><p>As the backup files (RDB or AOF) contains all the databases together, you cannot selectively restore one of them in event of crash or restart. You need to wait for the entire file dump to load in memory.</p>
</li>
<li><p>Like I used it — there were slightly different use cases in my project so I thought it’s better to separate the namespace but this could easily create the <a target="_blank" href="https://docs.aws.amazon.com/wellarchitected/latest/saas-lens/noisy-neighbor.html">noisy neighbour problem</a> and the monitoring tools can hardly differentiate between metrics based on database number.</p>
<p>  Due to all these reasons, even the creator of Redis recommends using different Redis instances rather than using different database numbers.</p>
</li>
</ul>
</li>
<li><p><strong>Replication</strong> - Redis supports asynchronous replication with extensive tunable parameters, but achieving reliability requires balancing latency, durability, and consistency:</p>
<ul>
<li><p>Replication is <strong>asynchronous</strong> by default, meaning replicas may lag or lose writes if the primary crashes before sync.</p>
</li>
<li><p>Use <code>repl-diskless-sync yes</code> for faster initial syncs and reduced I/O overhead; otherwise, RDB-based syncs (<code>repl-diskless-sync no</code>) can block during snapshot creation.</p>
</li>
<li><p>Tune <code>repl-backlog-size</code> and <code>repl-backlog-ttl</code> to maintain replication continuity across brief disconnects — preventing full resyncs.</p>
</li>
<li><p>Enable <code>min-replicas-to-write</code> and <code>min-replicas-max-lag</code> to protect against acknowledging writes when replicas are stale or disconnected.</p>
</li>
<li><p>Always monitor replication lag via <code>INFO replication</code> — even small lags can break consistency assumptions in queue or locking workloads.</p>
</li>
</ul>
</li>
<li><p><strong>Memory</strong> <strong>management</strong> - By carefully managing memory settings, you can ensure that Redis operates efficiently and reliably, whether used as a cache or for persistent storage.</p>
<ul>
<li><p>When using as a cache, it is useful to configure parameters like <code>maxmemory</code> and <code>maxmemory-policy</code> to evict data based on algorithms like LRU, LFU, etc. If there are no keys that match the criteria for eviction and maximum memory is reached, Redis will report errors for write commands.</p>
</li>
<li><p>When using for persistent storage, you should set <code>maxmemory-policy</code> to <code>noeviction</code> and leave 30-40% free memory. This buffer protects against fragmentation, background save forks, and transient spikes ensuring Redis never evicts queued jobs or stalls under memory pressure.</p>
</li>
</ul>
</li>
<li><p><strong>Tuning Data structures performance -</strong> There are certain parameters that influence how Redis internally represents its core data structures. Tuning them can significantly impact memory efficiency and CPU performance. Let’s understand these for a few data structures:</p>
<ol>
<li><p>Hashes</p>
<ul>
<li><p><code>hash-max-listpack-entries 512</code></p>
</li>
<li><p><code>hash-max-listpack-value 64</code></p>
</li>
</ul>
</li>
</ol>
</li>
</ol>
<p>        Redis stores small hashes in a compact, contiguous area for better cache locality. Once a hash exceeds either threshold (more than 512 fields or a field value longer than 64 bytes), Redis automatically converts it into a standard hash table. Increase these limits for smaller hashes to save memory; decrease if you have frequent large-field updates to avoid costly re-encodings. Instagram optimised this parameter for their use case back in 2011 (<a target="_blank" href="https://instagram-engineering.com/storing-hundreds-of-millions-of-simple-key-value-pairs-in-redis-1091ae80f74c">read more</a>).</p>
<ol start="2">
<li><p>Lists</p>
<ul>
<li><p><code>list-max-listpack-size -2</code></p>
</li>
<li><p><code>list-compress-depth 0</code></p>
</li>
</ul>
</li>
</ol>
<p>        Small lists are also stored as flat contiguous memory. <code>list-max-listpack-size</code> controls the maximum size of each listpack node; <code>-2</code> means Redis targets listpack nodes of roughly 8 KB. <code>list-compress-depth</code> defines how many nodes at the list’s ends remain uncompressed — <code>0</code> disables compression entirely. For read-heavy queues, keep compression low (<code>0–1</code>) for fast access. For archival or long lists, allow deeper compression (<code>2–3</code>) to save memory.</p>
<ol start="3">
<li><p>Sets</p>
<ul>
<li><p><code>set-max-intset-entries 512</code></p>
</li>
<li><p><code>set-max-listpack-entries 128</code></p>
</li>
<li><p><code>set-max-listpack-value 64</code></p>
</li>
</ul>
</li>
</ol>
<p>        Redis represents small sets of integers as <strong>intsets</strong>, and small generic sets as <a target="_blank" href="https://www.google.com/search?q=redis+listpack&amp;sca_esv=d07a5e3d0f6687b2&amp;biw=1728&amp;bih=958&amp;sxsrf=AE3TifM12JI2EmQxIefgnaQ9qREuPmXPeg%3A1760329284009&amp;ei=RH7saLslv6vj4Q-1sLWwBg&amp;ved=0ahUKEwi716TNqaCQAxW_1TgGHTVYDWYQ4dUDCBA&amp;uact=5&amp;oq=redis+listpack&amp;gs_lp=Egxnd3Mtd2l6LXNlcnAiDnJlZGlzIGxpc3RwYWNrMgoQABiwAxjWBBhHMgoQABiwAxjWBBhHMgoQABiwAxjWBBhHMgoQABiwAxjWBBhHMgoQABiwAxjWBBhHMgoQABiwAxjWBBhHMgoQABiwAxjWBBhHMgoQABiwAxjWBBhHSO4FULgDWOoEcAF4AZABAJgBtQGgAbUBqgEDMC4xuAEDyAEA-AEBmAIBoAIImAMAiAYBkAYIkgcBMaAH6wayBwC4BwDCBwMyLTHIBwY&amp;sclient=gws-wiz-serp"><strong>listpacks</strong></a>.<br />        Once a set exceeds these thresholds (by count or member size), it upgrades to a full hash table internally. Increase thresholds for workloads with many small sets to reduce overhead. Lower thresholds if set membership changes frequently to avoid repeated re-encodings.</p>
<p>    <strong>Note</strong>: I’ve heard of these configurations many times but never got a chance to optimise Redis for these use cases. Although these look like advanced configuration, these could be useful for you if your use-case revolve around specific data structures only and you’re running on a huge scale, it might be worth experimenting with these settings. Also, before playing around with configurations for any data structure, have a good idea about what all data structures you are using in your application. For example, Redis implements Geospatial data structures internally using sorted set (ZSET). So if you change configuration for <code>zset-max-listpack-entries</code> or <code>zset-max-listpack-value</code> it would also impact the performance and storage for Geospatial keys (incase you’re using those).</p>
<p>Beyond these, Redis exposes several other configuration parameters that impact</p>
<ul>
<li><p>Security (eg. <code>bind</code>, <code>protected-mode</code>, <code>requirepass</code>, <code>masterauth</code>)</p>
</li>
<li><p>Define clustering (for sharding data across nodes)</p>
</li>
<li><p>Performance (<code>timeout</code>, <code>tcp-keepalive</code>, <code>latency-monitor-threshold</code>)</p>
</li>
<li><p>Maintainability (<code>maxclients</code>, <code>loglevel</code>, <code>rename-command</code>), etc.</p>
</li>
</ul>
<h3 id="heading-redis-is-single-threaded-wait-what">Redis is single-threaded (wait... what?)</h3>
<p>Redis is famously known as a <strong>single-threaded</strong> data store — a design choice that often raises eyebrows among developers used to multi-threaded applications and databases. The assumption is natural: <strong>single-threaded means slow, right</strong>? In this case, the answer is <strong>“no — not really.”</strong> When Redis is called <em>single-threaded</em>, it means that only the <strong>core command execution loop</strong> is one single thread. All commands are executed sequentially, one after another. Each incoming request is parsed, executed, and responded to before moving to the next one — with no context switches or locking. This design avoids the biggest enemy of high-performance systems: <strong>concurrency overhead</strong>.</p>
<p>Despite being single-threaded, Redis is capable of handling hundreds of thousands of operations per second on a single core. Here’s why:</p>
<ol>
<li><p>Redis stores all data in RAM so there’s no disk I/O during command execution. Each operation typically completes in microseconds.</p>
</li>
<li><p>Since there’s only one command executor, there’s no need for fine-grained locking, atomic sections, or concurrency control — all commands are inherently atomic (hence safe for distributed locks).</p>
</li>
<li><p>Redis multiplexes I/O events in a single loop (similar to Node.js). This means one thread can serve thousands of connections efficiently.</p>
</li>
<li><p>Redis batches replies and writes them efficiently, minimizing expensive kernel transitions.</p>
</li>
<li><p>Operations like background save fork a child process does not impact the command execution directly (“indirect” impact is explained below).</p>
</li>
</ol>
<p>Redis proves that single-threaded doesn’t mean slow — when data lives in memory, network latency and locking costs matter more than CPU parallelism. Its design trades concurrency for deterministic speed, making it one of the fastest databases per core ever built. But there are some cases where command execution can be impacted:</p>
<ol>
<li><p>Redis supports Lua scripting but those are executed within same single-threaded event loop. So large/complicated lua scripts can slow down this thread.</p>
</li>
<li><p>Commands with high time complexity can slow down this event loop. For example: If you’re running <code>LRANGE</code> on a large list or <code>SMEMBERS</code> on a large set, it might take time to execute them and any commands that are fired afterwards. Redis official documentation mentions time complexity of all the commands.</p>
</li>
<li><p><code>DEL</code> command can be slow if you’re trying to free up large memory blocks synchronously. <code>UNLINK</code> can be used in those situations.</p>
</li>
<li><p>There’s a very popular and notorious command in Redis called <code>KEYS</code>. I took a production Redis down for a few seconds during my first encounter with Redis 😛. Because this iterates the entire keyspace and production systems (generally) have millions of keys, this command takes a lot of time to execute.</p>
</li>
<li><p>Background save for large data sets (&gt;10GB as per ChatGPT) can have some intermittent impact on the command execution thread.</p>
</li>
</ol>
<h3 id="heading-safely-analyzing-data-with-keys-command">Safely analyzing data with <code>KEYS</code> command</h3>
<p>There are multiple ways for doing this:</p>
<ol>
<li><p>First method is to avoid this as much as possible. Revisit your use case and check for alternate methods.</p>
</li>
<li><p>Redis documentation would recommend using <code>SCAN</code> instead of <code>KEYS</code> and use of the pointer (cursor) returned with each output to get next set of keys but you would need some application level logic to follow the cursor iteratively and filter keys by names incase you’re searching for some specific pattern of keys (eg. keys starting with word <code>alert</code>). For this specific purpose, I’ve tried a workaround:</p>
<ol>
<li><p>Setup a replica of this Redis.</p>
</li>
<li><p>Disconnect the replica from MASTER (command: <code>REPLICAOF NO ONE</code>).</p>
</li>
<li><p>Ensure there are no connections to this replica now, except your current session (use <code>CLIENT LIST</code> to see connected clients and <code>MONITOR</code> to see if someone is running any other command).</p>
</li>
<li><p>Now run <code>KEYS *</code> here so that there’s no impact elsewhere.</p>
</li>
</ol>
</li>
</ol>
<p>If your purpose is doing some analytics, somewhat old data might not cause any problems.</p>
<h3 id="heading-how-i-saved-75-cost-due-to-single-core-usage">How I saved 75% cost due to single core usage</h3>
<p>By now, we know two things about Redis:</p>
<ol>
<li><p>It’s uses a single CPU Core.</p>
</li>
<li><p>It’s an in-memory database. So for production machines, you need boxes with large amount of RAM.</p>
</li>
</ol>
<p>In one of my prev organisations, we had four Redis machines connected to a monolith application:</p>
<ol>
<li><p>One was for background queueing (full persistence needed, can’t lose data).</p>
</li>
<li><p>One was for some alert thresholds (persistence needed).</p>
</li>
<li><p>One was a pure cache use case (speed preferred)</p>
</li>
<li><p>One was for acting as a database for a high performance system (using lists/sets mostly) (persistence needed).</p>
</li>
</ol>
<p>Out of these, three were hosted on VMs (AWS EC2) and one on managed service (AWS Elasticache). The organisation was trying to cut costs from all sides and our own production infra was a significant component of that cost. After cutting all corners, we saw another opportunity — we could merge these Redices into one big EC2 machine. For some, this might sound like a stupid decision because it would mean a single point of failure for the application (i.e. if this EC2 goes down, everything goes down). But we still did moved ahead to this setup because cost cutting were our only targets.</p>
<p>We took a machine with 4 cores, ran four Redis processes on different ports and exported data from existing machines, saving <strong>75%</strong> on Redis costs. One learning that I got in this process is — you can’t create replica of a Elasticache Redis outside Elasticache ecosystem (basically EC2 cannot act as replica of Elasticache hosted Redis). So in order to copy data, we had to export it to S3 and copy that backup file on the new machine which increased our migration downtime by a few minutes.</p>
<h3 id="heading-deployment-options-and-cloud-providers">Deployment options and Cloud providers</h3>
<p>Redis can be deployed in several topologies, each balancing availability, consistency, and operational complexity differently. From a single instance to fully distributed clusters, the choice depends on whether your priority is simplicity, high availability, or scalability. I have mostly operated first two setups but I’ll share four of them for your reference:</p>
<ol>
<li><p><strong>Standalone:</strong> A single Redis instance handling all reads and writes. It is simple, fast, and ideal for development environments or small production caches with <strong>no fault tolerance</strong> if it goes down.</p>
</li>
<li><p><strong>Master–Replica:</strong> A primary node manages writes while replicas asynchronously copy data for read scalability and redundancy, offering better availability but <strong>manual recovery</strong> on failure.</p>
</li>
<li><p><strong>Sentinel:</strong> A lightweight layer that monitors master–replica setups, automatically promotes replicas during failures and updates clients, bringing high availability without clustering.</p>
</li>
<li><p><strong>Cluster:</strong> A distributed Redis setup that shards data across multiple masters with replicas for each, enabling horizontal scalability and built-in failover for large, production-scale workloads.</p>
</li>
</ol>
<p>Beyond this, I also wanted to touch upon an important thing that is kind of deployment - <strong>self managed</strong> vs. <strong>provider managed</strong>. For provider managed Redis services (eg. <strong>AWS ElastiCache</strong>, <strong>Azure Cache</strong> or <strong>GCP Memorystore</strong>), the provider takes care of the heavy lifting from provisioning to scaling, failover, patching, and monitoring, making them production-ready but that convenience comes with trade-offs in control and flexibility. Some reasons are:</p>
<ol>
<li><p><strong>Limited configuration control -</strong> You can only modify parameters exposed via their UI (eg. <em>parameter groups)</em>. Many performance and persistence knobs remain locked. One reason for this is that Redis license has changed multiple times since 2021 and the newer one doesn’t let the cloud provider run the original Redis as a managed service. So all of them fork Redis 6.x (till which license was relaxed) and maintain their own implementation. Because the API layer and protocol is compatible with the original Redis, developers hardly care about this fact.</p>
</li>
<li><p><strong>Restricted operational access -</strong> Direct shell access (<code>redis-cli</code>, <code>INFO all</code> with full privileges) is limited or read-only. You can’t inspect <code>/var/log/redis</code> or the underlying OS, so debugging memory fragmentation, CPU throttling, or swap usage becomes harder.</p>
</li>
<li><p><strong>Limited control during freezes or failovers -</strong> If a node hangs (e.g., fork stall, full memory pressure), you can’t SSH or restart the process. You must wait for the cloud provider’s control plane to detect failure and trigger replacement — which can take minutes. For production use cases, this can cause serious damage by the time this gets fixed. It did much damage for one of the teams I worked with, and they started hating Redis because of this.</p>
</li>
<li><p><strong>Version lag and feature delays -</strong> Managed offerings usually trail open-source Redis by a few releases. New features (e.g., active-active replication, ACL improvements, new data encodings) arrive late or in enterprise-only tiers.</p>
</li>
<li><p><strong>Higher cost for flexibility -</strong> While managed Redis saves operational effort, it’s significantly more expensive per GB of memory. Running Redis on self-managed EC2 is often 50% cheaper.</p>
</li>
</ol>
<h3 id="heading-popularity-and-alternatives">Popularity and alternatives</h3>
<p>For over a decade, Redis has been the standard choice for in-memory data store because of its speed, simplicity, and versatility. As of 2025:</p>
<ul>
<li><p>Redis continues to rank among the <strong>top 10 databases</strong> on the <a target="_blank" href="https://db-engines.com/en/ranking">DB-Engines index</a>, often #1 in the <a target="_blank" href="https://db-engines.com/en/ranking/key-value+store">key-value category</a>.</p>
</li>
<li><p>Major companies rely on it for both caching and primary storage of ephemeral workloads.</p>
</li>
<li><p>Virtually every cloud platform offers Redis (or compatible software) as a first-class managed service.</p>
</li>
</ul>
<p>While providers with managed Redis services have different forks of Redis due to <a target="_blank" href="https://redis.io/blog/what-redis-license-change-means-for-our-managed-service-providers/">license reasons</a>, there have been parallel initiatives by many open source and closed source teams leading to softwares which are 100% API compatible with Redis. To name a few:</p>
<ol>
<li><p><a target="_blank" href="https://docs.aws.amazon.com/memorydb/latest/devguide/what-is-memorydb.html"><strong>AWS Memory DB</strong></a> is a durable, in-memory, Redis-compatible database service that delivers ultra-fast performance with multi-AZ durability. MemoryDB stores all data in memory for sub-millisecond reads, but persists every write to a distributed transactional log (across multiple AZs in AWS). If the cluster or all nodes crash, MemoryDB can rebuild the in-memory state from that log — unlike ElastiCache, which would rely on periodic snapshots.</p>
</li>
<li><p><a target="_blank" href="https://microsoft.github.io/garnet/"><strong>Garnet</strong></a> is a research project by Microsoft Research. It uses the RESP wire protocol, making it compatible with standard Redis clients in many languages but unlike Redis’s single-thread command execution, Garnet’s storage layer is designed to scale across threads within a node. It can work over memory, SSD, and even remote storage (e.g., Azure storage) to support datasets larger than memory.</p>
</li>
<li><p><a target="_blank" href="https://dicedb.io/"><strong>DiceDB</strong></a> is an <strong>open-source, in-memory, reactive database</strong> written in Go. It is Redis protocol compatible while adding features like subscriptions / query reactivity (push updates rather than polling). It is designed to better utilize multi-core hardware rather than being stuck with a single-threaded event loop. I was following the creator of this project for a while, who had created this mostly for learning purposes and had not tested this on large number of production applications yet.</p>
</li>
<li><p><a target="_blank" href="https://www.dragonflydb.io/"><strong>DragonflyDB</strong></a> is a high-performance drop-in Redis replacement written in C++. It is fully API-compatible with Redis, supports multi-threaded execution, and often 2–3× faster in mixed workloads. It uses lock-free structures and shared-nothing architecture, avoiding Redis’s single-thread bottleneck. It is known to be extremely efficient with memory and CPU — ideal for modern multi-core servers.</p>
</li>
</ol>
<h3 id="heading-debugging-performance-bottlenecks">Debugging performance bottlenecks</h3>
<p>To ensure optimal performance of Redis in production, it's essential to monitor and debug performance bottlenecks effectively. Here’s a concise guide:</p>
<ol>
<li><p><strong>INFO Command</strong>: The <code>INFO</code> command in Redis is a versatile tool that provides a comprehensive overview of the server's status and performance metrics. When executed, it returns a wealth of information categorized into sections such as server, clients, memory, persistence, stats, replication, CPU, and keyspace. This command is invaluable for monitoring and diagnosing the health of your Redis instance. For latency analysis, the <code>INFO</code> command can help identify potential issues by providing insights into metrics like the number of connected clients, memory usage, and command statistics. High memory usage or a large number of connected clients can contribute to increased latency, and the <code>INFO</code> command helps pinpoint these areas.</p>
</li>
<li><p><strong>Slow logs:</strong> They are essential for identifying performance bottlenecks by tracking commands that exceed a specified execution time threshold. Configured through parameters like <code>slowlog-log-slower-than</code> and <code>slowlog-max-len</code>, slow logs capture detailed entries of long-running commands, including their execution time and timestamp. By analyzing these entries, you can pinpoint specific commands or patterns causing delays, guiding you in optimizing command performance or adjusting Redis configurations. I’ve used this a few times to figure out usage of some commands on large data structures causing slowness in command execution thread.</p>
</li>
<li><p><strong>Latency Doctor</strong>: Utilize the <code>LATENCY DOCTOR</code> command to analyze and report on latency spikes, providing insights into potential causes. When you run the <code>LATENCY DOCTOR</code> command, Redis examines its internal latency history and generates a report. This report includes details about various latency events, such as their frequency and potential causes. The command analyzes different aspects of the server's operation, including command execution, memory management, and I/O operations. Based on the insights provided by <code>LATENCY DOCTOR</code>, you can take specific actions to mitigate latency issues.</p>
</li>
<li><p><strong>Connection Pooling</strong>: Always implement connection pooling on the client side to manage Redis connections efficiently. This reduces the overhead of establishing new connections by reusing existing ones, which most client libraries support.</p>
</li>
<li><p><strong>Monitoring Tools</strong>: Employ monitoring tools that can instrument Redis metrics, such as Datadog, New Relic, or AWS CloudWatch. Key metrics to monitor include:</p>
<ul>
<li><p><strong>Memory Usage</strong>: Track used and peak memory to prevent out-of-memory errors.</p>
</li>
<li><p><strong>Command Latency</strong>: Monitor the time taken to execute commands to identify slow operations.</p>
</li>
<li><p><strong>Keyspace Hits/Misses</strong>: Analyze cache efficiency by observing the ratio of hits to misses.</p>
</li>
<li><p><strong>Evicted Keys</strong>: Keep an eye on the number of keys evicted due to memory limits, which can indicate the need for configuration adjustments.</p>
</li>
</ul>
</li>
</ol>
<p>By using these strategies and tools, you can effectively monitor and address performance bottlenecks in your Redis environment.</p>
<h1 id="heading-closing-note">Closing note</h1>
<p>Reflecting on my journey with Redis over the years, it's clear that this powerful tool has been more than just a component in my tech stack; it's been a constant companion. From my early days of grappling with security vulnerabilities to tweaking the nuances of Redis configurations, each experience has enriched my understanding and appreciation of this versatile database.</p>
<p>As I share these insights, my hope is that they resonate with you, whether you're just starting out or are well-versed in the world of Redis. Remember, while technical knowledge is crucial, it's the personal experiences and challenges overcome that truly shape our expertise. Here's to embracing the learning curve and continuing to explore the endless possibilities that such databases offer. Thank you for sparing time to read this.</p>
<hr />
<p>If you liked this post, you might also like these:</p>
<ol>
<li><p>Having worked with small teams mostly, I’ve been a proponent of keeping the tech stack very simple. I shared <a target="_blank" href="https://blog.gagan93.me/avoid-redundant-complexity">my thoughts</a> around the same.</p>
</li>
<li><p>I wrote a post early this year about <a target="_blank" href="https://blog.gagan93.me/habits-productivity-deep-work"><strong>habits, productivity and deep work</strong></a><strong>.</strong></p>
</li>
<li><p>If you’re transitioning from monolith to microservices and are looking for a scalable way to migrate data, read <a target="_blank" href="https://blog.gagan93.me/migrating-data-across-services"><strong>this post</strong></a> once.</p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[My financial journey]]></title><description><![CDATA[Background
I began working as a Software Engineer in June 2015, which means I've been earning for nearly 10 years. But if you ask me when I seriously started investing, the answer is "about three years ago." In this blog post, I'll share what happene...]]></description><link>https://blog.gagan93.me/my-financial-journey</link><guid isPermaLink="true">https://blog.gagan93.me/my-financial-journey</guid><category><![CDATA[personal finance]]></category><category><![CDATA[money]]></category><category><![CDATA[Investing]]></category><category><![CDATA[Financial Literacy]]></category><dc:creator><![CDATA[Gagandeep Singh]]></dc:creator><pubDate>Fri, 06 Jun 2025 11:46:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/joqWSI9u_XM/upload/15cff60f99edffb292ca16415af481ac.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-background">Background</h1>
<p>I began working as a Software Engineer in June 2015, which means I've been earning for nearly 10 years. But if you ask me when I seriously started investing, the answer is "about three years ago." In this blog post, I'll share what happened around that time and how my views on money and expenses have changed over the years. This article might draw some ideas from the book — <a target="_blank" href="https://www.amazon.in/Psychology-Money-Morgan-Housel/dp/9390166268">The Psychology of Money</a>, but I won't be acting as another financial influencer suggesting which stocks to buy 😃.</p>
<h1 id="heading-three-mistakes">Three mistakes</h1>
<p>My first salary was for about 16 days of work as I joined the company on 15th of June. About Rs. 20000 was credited to my account and my aim (before reaching home) was to take out some money to give to my parents. I was not a kind of person who would reach home with some gifts for everyone (as shown in movies 🤣). The only struggle for me was to find the ATM of the same bank so that I can set the PIN of debit card and then do the first withdrawal. While my new schedule was tiring (including travel, ramping up at work, etc.), being employed and having a steady income was definitely motivating. With this steady income, I did the following things (that sound like mistakes today):</p>
<ol>
<li><p>I wanted a new phone for myself but my sister also needed one. So I got one for her first (around August 2015) because the one I wanted was not yet launched. As both of us now had latest phones, I thought I should get one for my parents too. By September 2015, all four of us were having new phones. With a salary of ~ 40K/month, I got phones worth 55K within first four months to make everyone happy.</p>
</li>
<li><p>As my father thought that I should start investing, I met someone who did <a target="_blank" href="https://licindia.in/">LIC policies</a>. I got two policies done by them before end of financial year without doing any market research. In the next financial year, I took another one. Today, I have three running LIC policies that are mix of money back and long term policies. After that I also started putting some amount in PPF.</p>
</li>
<li><p>In 2019, I drove from <a target="_blank" href="https://www.google.com/maps/dir/delhi/amritsar/@30.272817,74.7001136,8.22z/data=!4m14!4m13!1m5!1m1!1s0x390cfd5b347eb62d:0x52c2b7494e204dce!2m2!1d77.2088282!2d28.6139298!1m5!1m1!1s0x391964aa569e7355:0xeea2605bee84ef7d!2m2!1d74.8722642!2d31.6339793!3e0?entry=ttu&amp;g_ep=EgoyMDI1MDEyOS4xIKXMDSoASAFQAw%3D%3D">Delhi to Amritsar</a> (and back) on our Hyundai Santro. After coming back, I thought of purchasing an automatic car without any strong reasons (okay, there was one reason — a good salary increment). I purchased a brand new Honda Amaze CVT. In last 6 years, I’ve driven it &lt; 27000 Kms.</p>
</li>
</ol>
<p>I think many of you might be having very similar stories of such expenses, so I hope you can relate with these. Although my parents always recommended me to save money and avoid useless expenses, they never gave any serious financial advice. And I think the core problem is that the system we are raised in expects “every good and sensible thing to come from elders”. Ideally, if it’s <em>your money</em>, then <em>you</em> should take care of investing it (not your parents). I’ll share my current perspective to the above expenses:</p>
<ol>
<li><p>The amount I spent on phones was &lt; 1.5month of my salary and half of it was on Credit card (because of No Cost EMI). To many of you, this might sound okay because even I have seen people earning a similar amount today, and still getting a iPhone Pro Max from their first cheque. While today I’m calling this as a mistake, it was not a very expensive mistake. And I think it was important for me to commit these mistakes so that I can learn from them and avoid making even bigger mistakes in future.</p>
</li>
<li><p>My father’s friend who did my LIC policies is a very good family friend and also a very good gentlemen. He did not hide any details of the policies from me or my father. Being a senior citizen (~70 yrs at that time), he recommended me to invest in safer options rather than in private companies or stock market. Because I was influenced by my father and his friend, I decided to go for safer options (like FDs, PPF and LIC). For first 3-4 years these were the only instruments that I was investing in. I even had a C.A. (who was of my age) but he never recommended me any good options and was happy filing my tax at the end of financial year. Investing a good amount in LIC policies seems like a mistake because the XIRR is very low. Although these policies have terminal benefits too, but that’s insignificant unless you die young.</p>
</li>
<li><p>I got a very good increment in Feb 2019 that made me think that I should upgrade our car. As my father also likes cars, he didn’t stop me 😆. Both of us started seeing some pre-owned cars and finally landed to the top model, automatic Honda Amaze that costed 9.5L at that time. My father was retiring in the same year so I thought I’ll use it to go to office later. With COVID kicking in and my remote role (since first COVID), we’ve not driven it much. Today, I think that purchasing a new car is the fastest way to burn hard earned post-tax salary (okay - not the fastest, having an AWS account is still the <a target="_blank" href="https://www.reddit.com/r/ProgrammerHumor/comments/xkadmh/150k_bill/">fastest</a>).</p>
</li>
</ol>
<h1 id="heading-from-2019-2022">From 2019 - 2022</h1>
<p>I met another CA through a common friend who started advising me to invest in slightly riskier options, given my young age and risk taking ability. The best thing about this person was that he didn’t act like policy agents or middlemen who recommended you specific funds or schemes to invest in. He focused on basic investing knowledge — the concept of <strong>asset classes</strong>, returns that beat inflation and possible investment options in the market (like stocks, mutual funds). These things were pretty new for me at that time. He convinced me to invest in Mutual funds but due to some issue with my Identity card (Aadhar) to mobile linking, this didn’t happen. I still got introduced to financial literacy, that I could learn more about.</p>
<p>That same year, my sister got engaged and married, and then COVID hit in early 2020, leading to salary cuts and a big drop in the market. Later in 2020, I got engaged too, and then married in 2021. After the wedding, my expenses went up for a few months (as you'd expect), and I started thinking seriously about investing. April 2022 was when I started my first set of <a target="_blank" href="https://www.hdfcfund.com/learners-corner/systematic-investment-plan?utm_source=google_search&amp;utm_medium=cpc&amp;utm_campaign=zklsip&amp;utm_term=self_help_seekers_generic&amp;utm_content=ad_copies_india&amp;gad_source=1&amp;gad_campaignid=22567274109&amp;gbraid=0AAAAApURn3O2RX66uK_SlBtcisM6QrkLg&amp;gclid=Cj0KCQjwgIXCBhDBARIsAELC9ZilSJjgPrzyaK7lcyH71FzXcutVFld6s4E63cNufHUgjxSsYH_l1jIaAiN5EALw_wcB">SIPs</a>.</p>
<p>In the past decade, financial literacy among salaried individuals has improved significantly. Back in 2015, most people I spoke with relied on traditional savings methods like FDs, RDs, PPF, and EPF. Now, even college graduates are trying their hand at trading (though that has a <a target="_blank" href="https://www.reddit.com/r/kolkata/comments/1e6clwd/the_ft_story_about_indian_youth_piling_into/">dark side</a> too). A major reason for this change is mobile-first platforms like Zerodha and Groww, which have made investing more accessible and user-friendly. As a result, terms like expense ratio, asset allocation, LTCG, and STCG are no longer alien. Additionally, financial influencers, or "finfluencers," have played a key role in educating the public, offering insights and tips on smart investing, and promoting a more informed approach to personal finance. I personally know folks who started earning in the past few years and are actively investing in stocks as well as IPOs. But again, this post is not to talk about which asset class is better so let’s talk about some fundamentals.</p>
<h1 id="heading-understanding-the-basics">Understanding the basics</h1>
<p>Managing money isn’t just about numbers, or creating plans on spreadsheets — it’s about <strong>behaviour, mindset, and self-awareness</strong>. You don’t need a finance degree to build wealth. What you really need is a good grip on a few timeless principles that most people overlook in the race for returns or status.<br />(Few of these draw ideas from Psychology of money that I read somewhere in 2023):</p>
<ol>
<li><p><strong>Spend less than what you earn</strong> - This is one of the most basic rules of personal finance, yet also the hardest to follow: don’t let your spending grow just because your income does. Real wealth begins when you resist that urge and continue living within your means. That doesn’t mean you need to give up all pleasures or avoid nice things—but if you don’t draw a line somewhere, you’ll always feel like it’s not enough, no matter how much you earn. Lifestyle upgrades can easily turn into a trap. The more you try to impress others, the more you link your happiness to appearances—and that’s a cycle that’s hard to break. When your self-worth depends on showing success, it can start to feel like you're constantly falling behind. True financial peace comes when you no longer feel the need to prove anything to anyone—not even yourself.</p>
</li>
<li><p><strong>Wealth is what you don’t see</strong> - Just because someone drives a fancy car doesn’t mean they’re rich. In fact, it might mean the opposite. Wealth is invisible — it’s not the <strong>money spent</strong>, but the money sitting quietly in savings or investments.</p>
</li>
<li><p><strong>Money is more about psychology than just numbers</strong> - The best financial plan isn’t the one with the highest returns. It’s the one you can stick to through good times and bad. Your ability to control impulses, stay patient, and avoid comparison matters more than technical knowledge.</p>
</li>
<li><p><strong>Save beyond goals</strong> -It’s great to have a recurring deposit or SIP in place for planned goals like your child’s education, a bigger home, or a new car. But life doesn’t always follow a plan. Many important or difficult expenses come without warning and that’s why you don’t need a specific reason to save. Saving gives you options, freedom and most importantly peace of mind when life takes an unexpected turn. Take layoffs, for example — they’ve become increasingly common and can affect anyone, regardless of their role or the size of the company. It’s never easy to navigate a layoff, especially in a tough job market. But the first thing you need during such a time is a financial cushion that helps you cover your expenses for the next few months. That safety net gives you breathing room to make thoughtful decisions, rather than desperate ones. Without it, people often experience serious mental stress and end up taking whatever job comes first (even if it pays less or doesn’t align with their goals).</p>
</li>
<li><p><strong>Don’t act like you’re in a race</strong> - Money related matters are personal. Our goals, life circumstances, and risk appetite are unique, and so are your financial decisions. For instance, I bought my first car at 26. Around the same time, a few people I know bought their first homes (something I still haven’t done). On paper, they invested in an appreciating asset, while I spent on something that loses value over time. But both choices had their own place and purpose in our lives. That’s why comparing financial decisions doesn’t really help. What matters more is being clear about <strong>your</strong> priorities and staying consistent with <strong>your</strong> plan. It’s easy to get influenced by what others are doing, but real financial progress comes when you stop reacting to others and focus on what makes sense for you.</p>
</li>
</ol>
<p>The above is a gist of what I learned from the books and blogs, but there are a few more personal lessons I want to share about saving and managing money:</p>
<ol>
<li><p><strong>We usually learn by making mistakes.</strong> It’s great if you can learn from other people’s mistakes, but when it comes to personal finance, most of us end up learning the hard way—after making a few of our own. It’s generally less costly to make money mistakes when you're younger and earning less, so don’t stress too much if you slip up early on.</p>
</li>
<li><p><strong>Have a clear idea of your monthly expenses.</strong> At the beginning of the month (when your salary comes), know exactly how much you need for essentials: groceries, bills, rent, small luxuries, vacations, or any planned expenses. Add a small buffer on top, and then invest the rest. If you wait until the end of the month to invest, chances are you’ll spend more than you intended. Money sitting idle in a savings account has a tendency to be consumed. Set up automatic transfers for SIPs, RDs, or any other investments, just like your EMIs to ensure disciplined investing.</p>
</li>
<li><p><strong>Build an emergency fund.</strong> Aim to set aside at least 4–6 months’ worth of expenses (not your salary). So if your salary is ₹2L per month but monthly expenses are ₹1L, your emergency fund should be at least ₹4–6L. Keep this in an easily accessible &amp; safe place like a fixed deposit or a similar liquid fund, so that you can access it quickly when something unexpected happens.</p>
</li>
<li><p><strong>Don’t ignore insurance—especially health insurance.</strong> If you're living in a Tier-1 city and your parents are aging or already retired, a medical emergency can shake your finances. Insurance won’t prevent health issues, but it’s a solid safety net when something unexpected happens. It’s not just about your parents; your own health cover also matters. Beyond this, if you can afford - do also get a term insurance.</p>
</li>
<li><p><strong>Plan ahead for annual expenses.</strong> Large annual expenses can feel like a sudden burden if you don’t prepare for them. For instance, if your parents’ health insurance premium is ₹48K per annum, it’s a lot easier to save ₹4K every month than to part with ₹48K in one go. This works well for school fees, festival travel, or any other similar large planned purchase.</p>
</li>
<li><p><strong>Think &amp; invest long-term.</strong> Once you’ve built your emergency fund, and have some financial stability, explore options for long-term investing based on your age and risk appetite. Mutual funds, for instance, aren’t great for short-term gains (except maybe debt funds), but over 10–20 years, the power of compounding really kicks in. The idea is to plant now and harvest later.</p>
</li>
<li><p><strong>Understand different asset classes and diversify.</strong> Inflation slowly eats away at your savings, so your goal should be to beat inflation, not just save. Learn about different investment options: fixed deposits, recurring deposits, ELSS, PPF, mutual funds, stocks, real estate, gold etc. Diversification protects your portfolio during market ups and downs.</p>
</li>
<li><p><strong>Look beyond shiny numbers, look at real returns.</strong> Few options may sound like a good investment but eat away at your returns. For example:</p>
<ul>
<li><p><strong>Gold:</strong> We’ve been buying gold for generations, but always buying jewellery as an investment isn’t smart. Making charges (10–20% or more) are sunk costs. If you buy something for ₹1.2L (₹1L gold + ₹20K making charges), you’ll only get ₹1L or less if you sell it immediately. If you want to invest in gold, consider gold funds or gold bonds.</p>
</li>
<li><p><strong>Mutual Funds:</strong> In a bull run, short-term returns might tempt you to sell early. But remember, STCG (Short-Term Capital Gains Tax) at 15% and exit load (varies per fund) can reduce your actual gains.</p>
</li>
<li><p><strong>Real Estate:</strong> Buying and selling property within 1–2 years can be expensive. Taxes, broker fees, registration charges, and the hassle itself often outweigh any short-term profit.</p>
</li>
</ul>
</li>
<li><p><strong>Stay curious and keep learning.</strong> Personal finance isn't a one-time thing you learn and forget. As you grow, your income, goals, and responsibilities evolve. Revisit your financial plan every 1–2 years. Read blogs, listen to podcasts, talk to financially savvy friends, or even consult a planner once in a while. The more you learn, the fewer regrets you'll have later.</p>
</li>
<li><p><strong>Start now, however small.</strong> Many individuals delay saving or investing, waiting for the "right time" or a higher salary. However, the truth is that the earlier you begin, the more advantageous it is for your financial future. Even if you start with small amounts, investing consistently over a long period can significantly increase your wealth. This approach allows your investments to benefit from the power of compounding, where the returns on your investments generate their own returns over time</p>
</li>
</ol>
<h1 id="heading-conclusion">Conclusion</h1>
<p>Money can feel overwhelming, but it really doesn’t have to be. A few intentional habits, some patience, and regular check-ins can go a long way. I’m still learning and making some mistakes. But it’s better to learn with a plan than to keep drifting without one. If you’re reading this, chances are you’ve had your own turning points too — maybe it was the first time you ran out of money before the month ended, or when you impulsively spent your bonus on something that didn’t feel worth it later. Whatever your journey looks like, I’m sure you’ve had moments of confusion, growth, or even regret when it comes to managing money. That’s perfectly normal—we’ve all been there in some way. The important thing is that we’re trying, learning, and doing a little better each year.</p>
<p>I’d love to hear your story too—whether it’s a mistake that taught you something valuable, a trick that helped you save more, or a mindset shift that changed the way you think about money. Drop your thoughts in the comments below.</p>
<h1 id="heading-references">References</h1>
<ol>
<li><p><a target="_blank" href="https://www.amazon.in/Psychology-Money-Morgan-Housel/dp/9390166268">Psychology of money</a></p>
</li>
<li><p><a target="_blank" href="https://www.youtube.com/watch?v=8nDjNn8ELCU">One idiot - Short film on importance of Financial Planning</a></p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Growth Through Unlearning]]></title><description><![CDATA[Notice if you’re describing a problem in terms of a solution you’ve already chosen — this can be a mental block for a lot of engineers. We start out by comparing problems to solve, but find ourselves talking in terms of technology or architecture we ...]]></description><link>https://blog.gagan93.me/growth-through-unlearning</link><guid isPermaLink="true">https://blog.gagan93.me/growth-through-unlearning</guid><category><![CDATA[learning]]></category><category><![CDATA[growth]]></category><category><![CDATA[Unlearn]]></category><category><![CDATA[innovation]]></category><dc:creator><![CDATA[Gagandeep Singh]]></dc:creator><pubDate>Sun, 26 Jan 2025 13:39:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/WE_Kv_ZB1l0/upload/04db6d1e9df54ad1659b75a4bf260050.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>Notice if you’re describing a problem in terms of a solution you’ve already chosen — this can be a mental block for a lot of engineers. We start out by comparing problems to solve, but find ourselves talking in terms of technology or architecture we “should" be using to make everything better.</p>
<p><strong>-Tanya Reilly, The Staff Engineer’s Path</strong></p>
</blockquote>
<h1 id="heading-introduction">Introduction</h1>
<p>In 2022-2023, I spent at least two afternoons each week discussing architecture with two senior colleagues at my company. We had hired many people, expecting growth, and decided to transition from a <a target="_blank" href="https://blog.gagan93.me/monolith-microservices">monolith to microservices</a>. During one discussion about a central authorization service, I kept drawing ideas from a system I had built in the monolith a few years ago. This is common among engineers: they often jump straight to solutions without fully understanding the problem, suggesting what they already know. This issue isn't limited to engineering teams; even product and design teams can overlook problem details and plan to create solutions similar to what they've done before.</p>
<p>Many industry leaders like hiring people with similar past experience, but that shouldn't stop you from finding better solutions. When you face a similar problem, some parts might be different, so the same solution might not work directly. If you overlook these small but important details, you might implement an unoptimized solution.</p>
<h1 id="heading-the-tale-of-two-rbac-systems">The tale of two RBAC systems</h1>
<p>In the past, I had developed a <a target="_blank" href="https://en.wikipedia.org/wiki/Role-based_access_control">Role-Based Access Control (RBAC) system</a> for a customer-facing application using SQL tables. This system was designed to manage user permissions efficiently by assigning roles to users and controlling access based on these roles. A few months after the implementation, we began optimizing the SQL queries for our application to enhance performance. During this process, we discovered that the tables associated with the RBAC feature were among the top 10 most frequently accessed tables. This was because the data from these tables was loaded before almost every API call, which significantly impacted the application's performance.</p>
<p>As an improvement, we decided to implement caching for this data. We set up a cache to store the this data and configured it to invalidate whenever a new role was assigned to a user. Since role assignments were not very frequent, this approach was feasible and effective. Within a month, we successfully eliminated 4-5 database queries that were previously executed @ 40 queries/sec.</p>
<p>A few years later, I encountered another RBAC system in a different application, and here were the differences between the two:</p>
<ol>
<li><p>The system was designed for internal users (instead of customers) and there was no chance that the customer side would need this functionality due to the nature of the business.</p>
</li>
<li><p>It was built using <strong>configuration files</strong> and <strong>one SQL table</strong>, instead of <strong>multiple</strong> <strong>SQL tables</strong> that use joins to get required data.</p>
</li>
<li><p>In the newer system, changing roles required a deployment because these configuration files were part of the repository code. In my old system, we had an admin page to update the database entry (and a callback to refresh the cache as well).</p>
</li>
</ol>
<p>When I first saw this new system, it seemed like a misnomer, but this is how it was perfect for their use case:</p>
<ol>
<li><p>Since this was built for internal users, there was no need to update the data often. Plus, the number of internal users was under a thousand.</p>
</li>
<li><p>There was no need for a caching layer because the configuration files were only loaded once, during app startup.</p>
</li>
<li><p>The team wanted to track changes to the permission system, and they thought using Git was the best way to do it 🤓. Sure, a maker-checker system could have been created for logging changes if SQL tables were used, but that requires separate effort.</p>
</li>
</ol>
<p>So which system is better? The one built on SQL tables or the one that mostly used configuration files 🤔?Take a moment to think before you continue reading.</p>
<hr />
<p>Seasoned engineers would sum it up in two words: <em>It depends</em> 😆.</p>
<p>Both systems worked well for their specific use cases without causing any issues for the end user. If I were to design the second system after creating the first one, I might have made the same mistake of using SQL tables and joins because that would have been the "standard solution" I remembered. But now, looking at both, I see that each was built according to its <strong>requirements</strong> without <strong>over-engineering</strong> for the future. In fact, <em>over-engineering</em> is very common in software engineering (been there, done that).</p>
<p>Again quoting from the <em>Staff Engineer’s Path</em>.</p>
<blockquote>
<p>An engineer who is not busy can be inclined to make work for themselves. When you see a vastly over-engineered solution to a straightforward problem, that’s often the work of a staff engineer who should have been assigned to a harder problem.</p>
</blockquote>
<p>For this issue, building a solution based on SQL tables would be considered <em>over-engineering</em> because:</p>
<ol>
<li><p>There was no need to update roles dynamically (without deployment).</p>
</li>
<li><p>Implementing the same on the SQL layer would call for a separate maker-checker system to monitor the changelog.</p>
</li>
<li><p>Additionally, future work might involve developing a caching layer since the data rarely changed.</p>
</li>
</ol>
<h1 id="heading-comfort-resists-innovation">Comfort resists innovation</h1>
<p>Over the past few years, a notable portion of my work has focused on simplifying systems that have become increasingly complex due to ongoing product changes implemented by various developers. As I work on enhancing these systems, it is crucial to first gain a thorough understanding of their current behavior. This often involves interviewing individuals who have been involved with the system for a long time. These individuals tend to be very <strong>familiar</strong> and <strong>comfortable</strong> with the system as it stands, having witnessed the gradual increase in complexity and understanding the reasons behind each modification.</p>
<p>This sense of <strong>comfort</strong> can be problematic because it prevents them from envisioning a newer, more streamlined version of the system that would be easier for everyone to comprehend. Their familiarity with the existing complexity, combined with limited exposure to effective design principles, often leads them to believe that the current state is the optimal way to construct the system. This mindset results in a lack of motivation to pursue <a target="_blank" href="https://blog.gagan93.me/refactoring-lessons-learnt-so-far#heading-keep-changes-small-and-reversible">incremental improvements</a> that could make the system more efficient and user-friendly. Consequently, they may not plan or implement small, reversible changes that could gradually enhance the system's design and functionality.</p>
<p>In this case, <strong>unlearning</strong> is important so that you can think of the system from a fresh perspective and think of an alternate, simplified architecture. This would help you:</p>
<ul>
<li><p>To recognize that some of what you "know" might not be <strong>universally correct</strong> or <strong>applicable everywhere</strong>.</p>
</li>
<li><p>To let go of <strong>fixed patterns</strong> that prevent you from adapting to new situations. In order to grow, it’s important to not rely on such patterns.</p>
</li>
<li><p>To shed the confidence in solutions or methods simply because <strong>they worked in the past</strong>. Assumptions based on past experiences can lead to flawed conclusions or solutions when circumstances change.</p>
</li>
</ul>
<h1 id="heading-some-personal-examples">Some personal examples</h1>
<p>Software Engineers need to constantly unlearn the old concepts so that they can build efficient systems. Throughout my career, I've encountered numerous instances that have taught me the importance of being willing to <strong>unlearn</strong> outdated methods.</p>
<p>These are some instances where <strong>unlearning</strong> helped me:</p>
<ol>
<li><p>If you're used to working with monolithic systems and switch to microservices (or join a team using microservices), you need to unlearn some habits and adopt new ones. In a monolithic system, you rarely deal with timeouts because all the data is usually in one database that your app is always connected to. But in a system that constantly gets data from other services, you have to consider timeouts, retries, and fallbacks (if available). You'll also learn new concepts like circuit breakers, centralized logging, inter-service authentication, etc. If you stick to the old monolithic way of coding in a microservices setup, you'll create inefficient solutions. (Learn more <a target="_blank" href="https://blog.gagan93.me/monolith-microservices">here</a>).</p>
</li>
<li><p>The simplest and slowest way to build a module is to execute all statements sequentially. For instance, in an e-commerce system, when an order is placed, tasks like sending notifications to the user, notifying the warehouse, and processing the payment are often performed one after the other. This sequential approach can cause inefficiencies and delays. By adopting asynchronous processing frameworks, you can enhance performance by allowing each step to be handled independently and concurrently. It's crucial to unlearn traditional methods and embrace the intricacies of asynchronous distributed systems to develop scalable and efficient solutions.</p>
</li>
<li><p>My first company operated in a "work from office" mode. During my second role, we transitioned from being "in-office" to "work from home" due to the COVID pandemic. Now, I am part of a fully remote team with members spread across different time zones. The skills required to thrive in these varied work environments differ significantly. For instance, in a remote setting, effective written communication is crucial to ensure that messages are clear and not misinterpreted by colleagues from diverse cultural backgrounds. Additionally, it's important to recognize that team members may not always be available simultaneously. Therefore, it's essential to plan your workday strategically. Tasks requiring input from others in different time zones should be scheduled when they are available, while you can focus on tasks that can be completed independently during other times. This approach helps maintain productivity and ensures smooth collaboration across the team.</p>
</li>
<li><p>The transition from on-premise to cloud virtual machines, and now to Kubernetes pods, has introduced a more ephemeral nature to what we consider a <em>server</em>. I recall a high-rate ingestion service in our old organisation that buffered data temporarily to disk when the consumer was down. This application ran on AWS EC2 machines, which are less ephemeral than Kubernetes pods, and the design worked well despite using <a target="_blank" href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/InstanceStorage.html">instance stores</a> that do not persist through EC2 stop/start cycles. In a Kubernetes environment, designing this would require a different approach because pods can be terminated for various reasons and with higher frequency. To handle this, you might need to implement persistent storage solutions like using Persisted volumes, StatefulSets or external storage systems such as Amazon EBS to ensure data durability and availability when pods are frequently restarted or rescheduled. This shift necessitates a rethinking of how data persistence and application state are managed in cloud-native architectures.</p>
</li>
</ol>
<p>Unlearning old habits was crucial for my growth as it allowed me to adapt to new technologies and methodologies. By shedding outdated practices and embracing continuous learning, I was able to contribute to the development of more efficient and scalable systems.</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>In this brief blog post, I've highlighted the significance of <strong>unlearning</strong> and its vital role in fostering innovation and adaptability for professionals across various fields. By letting go of preconceived notions and outdated solutions, we can tackle problems with fresh perspectives, resulting in more efficient and context-appropriate outcomes. Embracing unlearning as a practice can significantly contribute to personal and professional growth, enabling individuals to stay agile and responsive in an ever-evolving landscape.</p>
<hr />
<p>If you liked this post, please read these too:</p>
<ol>
<li><p>This is my second blog post related to <strong>growth</strong>. The first one talks about <a target="_blank" href="https://blog.gagan93.me/habits-productivity-deep-work">habits, productivity and deep work</a></p>
</li>
<li><p>It’s important to manage your time when you’re thinking about growth. I shared <a target="_blank" href="https://blog.gagan93.me/cost-of-time">my thoughts</a> on the same an year ago.</p>
</li>
<li><p>If you’re transitioning from monolith to microservices and are looking for a scalable way to migrate data, read <a target="_blank" href="https://blog.gagan93.me/migrating-data-across-services">this post</a> once.</p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Habits, Productivity & Deep work]]></title><description><![CDATA[Background
I resigned from my first company in December 2018 when I decided to switch from a services company to an early-stage product startup. Although the previous company wasn't treating me badly, there was a trend of people leaving after 2-4 yea...]]></description><link>https://blog.gagan93.me/habits-productivity-deep-work</link><guid isPermaLink="true">https://blog.gagan93.me/habits-productivity-deep-work</guid><category><![CDATA[habits]]></category><category><![CDATA[Productivity]]></category><category><![CDATA[deep work]]></category><category><![CDATA[growth]]></category><dc:creator><![CDATA[Gagandeep Singh]]></dc:creator><pubDate>Sun, 05 Jan 2025 12:59:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/KE0nC8-58MQ/upload/f2d0f9722efe547386baa78fba0edc2a.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-background">Background</h1>
<p>I resigned from my first company in December 2018 when I decided to switch from a <strong>services company</strong> to an <strong>early-stage product startup</strong>. Although the previous company wasn't treating me badly, there was a trend of people leaving after 2-4 years, which was on my mind, along with other factors. My previous project, which I worked on for more than 2.5 years, was almost wrapped up, and I was being assigned random tasks that I didn't enjoy. Finally, I joined this product startup, <a target="_blank" href="https://loconav.com/">LocoNav</a>, on February 5th, 2018.</p>
<p>While onboarding at my current organization, <a target="_blank" href="https://branchapp.in/">Branch</a>, took almost a week due to extensive documentation and mature processes, the situation at LocoNav was completely different at that time. Within the first four hours, I was logged into their production server using my new laptop to check a <a target="_blank" href="https://www.crowdstrike.com/en-us/cybersecurity-101/next-gen-siem/log-rotation/">log rotation</a> issue. This might seem unusual to those who haven't experienced the early startup environment, but for those who have, it's not surprising. You need to be productive from day one and ensure your efforts align with everyone else's. With all this hustle came a lot of stress, and there were many reasons for this stress. Here are a few:</p>
<ol>
<li><p>I had moved from a services company. Although it was also a startup, its processes and hierarchies were more formal. Sometimes, I would think, <em>We should have the "xyz process" from my old company to avoid this problem</em>.</p>
</li>
<li><p>Not just me, but most of us came from service or stable companies. So, working at this fast pace was totally new for us. Everyone found it tough to deal with the uncertainty, juggling different tasks, and the ever-changing requirements. When I joined, there were no product managers. So, we were building a <strong>product</strong> without a <strong>product manager</strong>.</p>
</li>
<li><p>I aimed for perfection in many tasks, and that was a bit of a mistake. I didn't realize that in early-stage startups, much of what we build might be discarded, so aiming for perfection from the start isn't necessary. Trying to balance perfection and delivery, I ended up stressing myself and sometimes overworking. (Though many APIs I wrote in 2018-19 are still running in their production 😀).</p>
</li>
</ol>
<p>In this blog, I’m sharing what I've learned from working in both early-stage and mature startups. This will cover:</p>
<ol>
<li><p>Finding a balance between <strong>perfection, speed</strong>, and the <strong>stress</strong> of delivering.</p>
</li>
<li><p>How much <strong>context-switching</strong> is beneficial and its impact on your <strong>productivity</strong>.</p>
</li>
<li><p>The value of <strong>deep work</strong> and how most of our jobs require it, but our environment isn't set up for it.</p>
</li>
<li><p>How your <strong>habits</strong> and <strong>environment</strong> affect your work.</p>
</li>
</ol>
<p>Many of these insights are common to any corporate role. However, based on my experience as a Software Engineer, most examples will pertain to software design. So, let’s start 🚀</p>
<h1 id="heading-perfection-amp-speed">Perfection &amp; Speed</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735896963741/8a1b1833-a2b9-4592-8ec4-50e21bf889e5.jpeg" alt class="image--center mx-auto" /></p>
<p>I was quite a perfectionist when I joined LocoNav. Out of stress, I would write emails to my founder, VP, and a few teammates, sharing my thoughts and asking for their suggestions on the problems we were facing. These weren't product-related issues but suggestions on solving team problems (e.g., <em>Should we hire more?</em>) or individual problems (e.g., <em>How to improve productivity?</em>). While everyone appreciated my efforts to address these issues, not everyone was interested in solving them. Everyone has a unique perspective, and what seemed problematic to me might have seemed normal to someone else, possibly due to their past experience with similar situations.</p>
<p>The question for the need of perfection is tricky, and is very subjective to your current working environment. For example:</p>
<ol>
<li><p>If you're a billion-dollar stock exchange firm or bank, perfection in your work is a basic requirement.</p>
</li>
<li><p>If you're a startup dealing with money, most of your work needs to be perfect. However, your idea of <em>perfection</em> will likely be <strong>less strict</strong> than that of a billion-dollar company because your processes are less mature.</p>
</li>
<li><p>If you're a company manufacturing medical equipment, even one defect can be dangerous or life-threatening. So, your standards for perfection will be very high.</p>
</li>
<li><p>If you're a company not heavily regulated due to the nature of your business, your standards for perfection might not be very high. Doing things in a better way takes time and money, and no one wants to spend that money unless necessary.</p>
</li>
</ol>
<h3 id="heading-do-you-really-need-perfection-everywhere">Do you really need perfection everywhere?</h3>
<p>I think most of the companies that build something new or cutting edge do not face a lot of compliance (at least initially). For example:</p>
<ol>
<li><p>Stock companies that started between 2010-2020 can show how compliance has become stricter over time. In 2010, they didn't take all the measures they do now. Each step requires time and money, which startups often lack.</p>
</li>
<li><p>SaaS companies in Europe, or those handling European user data, had to change how they store, process, and delete user data after GDPR was introduced. Making these changes is expensive, especially for large products.</p>
</li>
<li><p>Many AI startups have been creating amazing products since ChatGPT appeared, but there isn't a governing body to set limits on AI use to ensure it's safe. For example, the CTO of OpenAI <a target="_blank" href="https://www.youtube.com/watch?v=lS0G2D6MKGw">wasn't sure</a> if YouTube's public videos were used to train Sora, their video generation model.</p>
</li>
</ol>
<p>Startups often operate in fast-paced environments where speed and innovation are prioritized over strict processes and documentation. Without external regulatory requirements setting clear quality benchmarks, teams lean towards quick fixes and ad-hoc solutions rather than building for long-term stability and scalability. This lack of enforced structure can lead to inconsistent practices, technical debt, and overlooked edge cases in both product and operational workflows. No one wants to shutdown their company or go to jail for doing something that’s illegal (at least, most of us don’t want to!). So if you’re under a governing body, ensure perfection accordingly. Otherwise feel free to experiment and take some risks (or technical debts).</p>
<p>I think this gives you an idea that the level of perfection depends on kind of business you’re building.</p>
<h1 id="heading-stress-amp-context-switching">Stress &amp; Context switching</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735896654581/12e87cef-e876-4a66-8761-8670b4b1df19.png" alt class="image--center mx-auto" /></p>
<p>Handling stress in a startup requires balance and resilience. In the early days, we worked six days a week, with one day from home and five in the office. This wasn't a set rule, but many of us followed it. I didn't burn out quickly, but I made sure to rest when needed. Negotiate and align priorities with your teammates and manager because time is the most valuable asset for a startup. It's crucial to have leaders who are supportive and practical. While everyone is expected to work quickly and deliver almost daily, not everyone will be a 5x or 10x developer.</p>
<p>I remember many days when I would come home utterly exhausted, unable to even hold a conversation with anyone. My family was concerned and thought I had made a mistake by leaving a stable job to join a startup. At that time, I also had little idea of what the future held for me. Despite this, I was driven by the motivation and support I received from my team and the leaders around me. There were two major sources of stress during this period:</p>
<ol>
<li><p>In the first 3 months, I mainly worked on building the API layer for their new Android app. We were designing screens, building APIs, and developing the mobile frontend all at once. This simultaneous work caused a lot of back and forth, leading to <strong>rework</strong>. While redoing tasks is common in startups, it caused frustration for everyone. Too much parallel work can be counterproductive, showing our inexperience with working at such a fast pace.</p>
</li>
<li><p>Since everyone needed to handle multiple roles and most things were unstable, I was involved in many tasks besides just building the API layer. I had to switch between different tasks, which sometimes meant I accomplished <strong>nothing concrete</strong> on a particular day. This was very discouraging for me.</p>
</li>
</ol>
<p>One clear lesson I learned from observing my day was that to <strong>make an impact</strong>, I needed to <strong>prioritize</strong> tasks. In my previous role, this kind of prioritization wasn't necessary because things were stable, and the layers of management above me handled it. Gradually, I became someone who learned to say ‘<strong>No</strong>’ when something couldn't be done in a given time frame or when I was already overloaded with tasks. In some teams, this might be seen negatively, but in our small team, where everyone was making honest efforts, people understood that if I said <em>no</em>, it truly meant I was busy.</p>
<h3 id="heading-can-we-get-a-lot-done-with-lots-of-context-switching">Can we get a lot done with lots of context switching?</h3>
<p>The short answer is “No”, but I have two takes here. Both of these conclusions come from my personal experience and what I learnt by reading.</p>
<ol>
<li><p>If you often switch between tasks while working, you might initially feel more stressed and frustrated, as you'll see small progress in many tasks but nothing substantial by the end of the day. Over time, you'll learn to choose fewer tasks and complete some of them. From my personal experience, trying to handle 10 tasks a day was never effective for me. However, focusing on 3-5 tasks that needed minimal input before completion made me happier. When starting something new, I found that I needed 2-4 uninterrupted hours (without <strong>context switching</strong>) to create a solid plan for the coming days or weeks. I've worked closely with people who can't switch between tasks as efficiently as I do. This behaviour is explained by Robin Sharma in the book "<em>The 5 AM Club</em>", where he highlights the brain's amazing <strong>neuroplasticity</strong> (an ability to adapt, rewire, and grow with consistent effort and habits). So, context switching works for me because my mind has <strong>adapted</strong> to this pattern over months and years of practice.</p>
</li>
<li><p>Not everyone can handle a lot of stress, so not everyone can switch tasks efficiently. It's wrong to expect everyone to multitask well. I've written about efficient multitasking in a <a target="_blank" href="https://blog.gagan93.me/multi-tasking-is-not-so-cool">separate blog</a> if you want more details. As a colleague, if I see someone struggling with multiple tasks, I always recommend prioritizing a few and completing those instead of making little progress on all of them.</p>
</li>
</ol>
<h1 id="heading-deep-work">Deep work</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735896547873/9f01f510-d375-470f-9e4d-7e56362c5f5c.jpeg" alt class="image--center mx-auto" /></p>
<p>I've been working remotely since March 2020, so I stay connected with my colleagues through instant chat apps like Slack. When Facebook was launched in 2004, "online connections" were seen positively, but over time, people realized how addictive these sites can be. While you can cut down on using apps like Facebook, Instagram, Twitter, and Snapchat, you can't stop using tools like Slack, Zoom, Meet, and Teams because you're expected to be <strong>online</strong> during work hours and respond to calls and messages. You might be wondering - <em>Why is this guy comparing Facebook to Slack?</em></p>
<p>The answer is simple - all these apps steal your attention and reduce your ability to perform <strong>Deep Work.</strong> Apps like Slack are designed with instant notifications and visual cues (eg. red badges and notification sound) that trigger our brain. Each notification creates a sense of urgency, pulling us away from focused tasks. The fear of missing out (FOMO) on important updates further compels us to check messages frequently. Additionally, the platform's design encourages constant context-switching, <strong>fragmenting our attention</strong> and <strong>reducing cognitive capacity</strong> for deep, uninterrupted work. Over time, these repeated interruptions diminish our ability to <strong>focus deeply</strong>, making us more reactive and less capable of sustained, meaningful problem-solving. If you find it challenging to advance in your role, your <strong>weak attention span</strong> might be a significant factor. Referring to the career progression of software engineers, it's relatively straightforward to advance from a Software Engineer to a Senior Software Engineer. However, progressing beyond the Senior Engineer level requires demonstrating a <strong>broader impact</strong>, which can only be achieved through focused, uninterrupted time dedicated to cognitively demanding tasks. Deep work fosters a systems-level perspective, enabling the identification of patterns, anticipation of potential challenges, and proposal of innovative improvements. It also provides the opportunity to learn and refine expertise in critical areas that contribute to organizational success. Without consistent deep work, engineers may become ensnared in reactive workflows, unable to deliver the strategic value expected at the Senior+ level.</p>
<h3 id="heading-do-our-jobs-encourage-deep-work">Do our jobs encourage Deep work?</h3>
<p>Unfortunately, many work environments, especially startups, don't really encourage deep work. With tasks often feeling urgent, we tend to reward <strong>visibility over impact</strong>, which unintentionally promotes shallow work, like responding to emails, attending long meetings, or giving constant status updates. These activities can create an illusion of productivity because they are <strong>easy to measure</strong> and <strong>immediately visible</strong>. On the other hand, the focused, uninterrupted effort needed for solving complex problems often doesn't show immediate results and can be overlooked in places focused on short-term goals. In my opinion, we need more mature managers who appreciate deep work for its long-term benefits.</p>
<h3 id="heading-some-personal-productivity-hacks">Some personal productivity hacks</h3>
<p>These habits have helped me to get more work done without overworking:</p>
<ol>
<li><p>Whether it's email or Slack, I've set up filters everywhere. If you're in a channel that no longer interests you, or if there are emails you're not interested in but are sent to a group, create filters for them. Check these messages occasionally instead of getting notified every time a new message arrives. Similar optimisations include muting channel notifications, leaving those channels, unsubscribing to some newsletters that you no longer need, or setting up email filters to ensure emails with certain subjects or sender IDs don't land in your inbox (allowing you to check them later).</p>
</li>
<li><p>I typically begin my workday around 8:00 - 8:30 am. This routine has been a part of my life for more than five years now, and it has significantly contributed to my ability to focus on crucial tasks before the rest of the team starts their day (given flexible working hours and distributed teams).</p>
</li>
<li><p>Although I get a lot done in the morning, I also set up Do Not Disturb (DND) on Slack when needed. It's better to clearly indicate that you're busy rather than ignoring messages.</p>
</li>
<li><p>I keep my mobile internet off during work hours (except at lunch) to avoid distractions from push notifications.</p>
</li>
<li><p>I keep a simple text file open in my editor all the time. It's got a list of stuff I need to focus on, plus all the raw notes about how things are going. Since the list covers everything from my own tasks to PR reviews, design document reviews, and other things, it's my go-to spot for figuring out what to do next. Prioritizing is just a matter of moving a few lines around. This setup also makes it easy to pick up where I left off after vacations or long weekends without feeling overwhelmed or lost.</p>
</li>
<li><p>I push all shallow work to a specific part of my day. These are minor tasks with medium to low priority, which I can tackle after completing deep work. On another note, there are often tiny tasks that take less than two minutes. I choose to do these tasks immediately because scheduling them actually wastes more time.</p>
</li>
<li><p>I maintain a clutter-free physical workspace, ensuring that my desk and surrounding area are organized and tidy. This helps me focus better and reduces distractions. Additionally, I relocated to a quieter place a few months ago, which has significantly improved my ability to concentrate and work efficiently (more details in the next section).</p>
</li>
</ol>
<h1 id="heading-habits-and-environment">Habits and environment</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735891783323/fff70d91-fd73-41cc-be62-fab9ab2bcf93.jpeg" alt class="image--center mx-auto" /></p>
<p>Your room is a reflection of your mind. The state of your room, whether it's organized, cluttered, or decorated in a specific way, can often reveal aspects of your personality, thought process, and overall mental state. A tidy room signifies a clear mind and a messy room indicates a more chaotic inner world. I’m sharing this because I moved to a cleaner workspace in October 2024, and it has improved my focus. Having worked from home for a long time, it was important for me to set up a space away from regular household distractions, which I had become accustomed to working with.</p>
<p>During the time I set up my new desk, I added three books to my bookshelf: <strong>Atomic Habits</strong>, <strong>Deep Work</strong>, and <strong>Zero to One</strong>. While the third book focuses on entrepreneurship, the first two helped me adjust my <strong>habits</strong> and <strong>work patterns</strong> to be more productive.For example, I used to struggle to read regularly and would save reading for the weekends because my weekdays were busy. However, in <em>Atomic Habits</em>, the author shares a simple but useful tip:</p>
<blockquote>
<p>It’s easy not to practice the guitar when it’s tucked away in the closet. It’s easy not to read a book when the bookshelf is in the corner of the guest room. It’s easy not to take your vitamins when they are out of sight in the pantry. When the cues that spark a habit are subtle or hidden, they are easy to ignore.</p>
</blockquote>
<p>He suggests placing a book under your pillow to encourage more frequent reading, and that's exactly what I did. In the morning, I would wake up and spend 10 minutes reading, repeating the same before bed. I thought I was "busy" during the week, but making this small change wasn't difficult since I was using that time to scroll through social media anyway. As a result, I finished reading these three books in just <strong>9 weeks</strong> (something that would have taken me at least 16-20 weeks if I only read on weekends). Some days, I got so interested in the topic that I spent 20-30 minutes reading instead of just 10. It was definitely better than spending those 20-30 minutes on Instagram 😉. This habit lasted for about 4 months, but now the streak is broken, so I read 3-4 nights a week instead of 6-7 (still much better than just weekend reading). One reason for this change is that I switched back to technical books, which require more focus and the right mood to read.</p>
<p>While it's important to have good habits, it's even more crucial to learn <strong>how to fit them</strong> into your schedule, so you don't struggle with prioritizing what's important. Reading <em>Atomic Habits</em> really helped me with that. Let me share two more habits I've developed over the years that I believe have helped me grow:</p>
<h3 id="heading-boredom-is-important"><strong>Boredom is important</strong></h3>
<p>Many people who struggle to grow beyond a certain point often claim they are extremely busy—whether genuinely or by filling their time with low-impact tasks that create an illusion of "busyness." While constant activity might feel satisfying, it often leaves little room for <strong>strategic thinking or mental clarity</strong>. Surprisingly, boredom plays a vital role in growth. It’s in these quiet, unoccupied moments that your mind can wander, reflect, and make unexpected connections. Boredom is not idleness. it’s about giving your brain the space to process thoughts, spark creativity, and identify priorities. Without moments of stillness, you risk being trapped in a cycle of reactive tasks, never pausing to think deeply or plan effectively for the future. Sadly, social media apps <a target="_blank" href="https://blog.gagan93.me/cost-of-time#heading-technology-addiction">eliminate boredom</a> by constantly feeding quick dopamine hits through endless scrolling, notifications, and bite-sized content. This prevents our minds from wandering, reflecting, or engaging in deeper thought.</p>
<h3 id="heading-simplicity-is-clarity">Simplicity is clarity</h3>
<p>This habit is deeply rooted in my mindset, drawing inspiration from both <strong>religious beliefs</strong> and <strong>practical experiences</strong>. On the religious side, there’s an emphasis on leading a <strong>simple and minimal life</strong>, free from unnecessary complications. This perspective has taught me the value of clarity &amp; focus, which extend far beyond personal life. Similarly, as an engineer, simplicity isn’t just a preference while building large systems — it’s a functional necessity. Simple systems are inherently easier to understand, debug, and improve. They lower the cognitive load on those working with them, allowing teams to collaborate effectively, onboard faster, and make informed decisions with confidence. On the flip side, I’ve also worked on (and even built 🫣) systems that were architected in a way that they were not only hard to understand but harder to extend. So seek simplicity as a principle in every aspect of your life.</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>In this article, I shared my journey transitioning from a services company to an early-stage product startup, highlighting the challenges and lessons learned along the way. We explored the delicate balance between perfection and speed, the impact of stress and context switching on productivity, and the importance of deep work for personal and professional growth. Additionally, I shared personal productivity hacks and the significance of habits and environment in enhancing work efficiency. By reflecting on these experiences, I hope to provide valuable insights for those navigating similar paths in dynamic work environments.</p>
<p>Before ending this blog post, I’d like to share photos of my <strong>existing setup</strong> (that was in the middle of our living room) and <strong>current setup</strong> (isolated on a separate floor where I use to work out during COVID).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736066762100/19a52513-535d-474f-b764-8513374d9258.jpeg" alt class="image--center mx-auto" /></p>
<h1 id="heading-further-reading">Further reading</h1>
<ol>
<li><p>If you’re interested to seriously read about <a target="_blank" href="https://www.amazon.in/dp/0349413681?ref=ppx_yo2ov_dt_b_fed_asin_title">Deep work</a> or <a target="_blank" href="https://www.amazon.in/dp/1847941834?ref=ppx_yo2ov_dt_b_fed_asin_title">Habits</a>, and their impact on your life, read the respective books.</p>
</li>
<li><p>I’ve shared my opinions about <a target="_blank" href="https://blog.gagan93.me/multi-tasking-is-not-so-cool">multi-tasking</a> some time ago on my blog.</p>
</li>
<li><p>Despite working in startup throughout my career, I hated overworking (and almost never did it). In a <a target="_blank" href="https://blog.gagan93.me/eight-productive-hours">blog post</a>, I shared common reasons that cause you to work beyond eight working hours.</p>
</li>
<li><p>Apart from <a target="_blank" href="https://blog.gagan93.me/tag/interview">Interview related content</a> (that’s always most viewed), there are plenty of technical blog posts that I’m sure you’ll like (few of my favourites are <a target="_blank" href="https://blog.gagan93.me/avoid-redundant-complexity">this</a>, <a target="_blank" href="https://blog.gagan93.me/cloud-pricing-vendor-lock-ins">this</a> and <a target="_blank" href="https://blog.gagan93.me/things-code-reviewers-hate">this</a>)</p>
</li>
</ol>
<hr />
<p>Thank you for reading this. Please leave your thoughts in the comments 😊</p>
]]></content:encoded></item><item><title><![CDATA[Refactoring: Lessons Learnt So Far]]></title><description><![CDATA[Background

Refactoring is the process of restructuring code, without changing its original functionality.

Over the past three years, my role has allowed me to pick a lot of refactoring tasks, which have enhanced my skills and understanding around R...]]></description><link>https://blog.gagan93.me/refactoring-lessons-learnt-so-far</link><guid isPermaLink="true">https://blog.gagan93.me/refactoring-lessons-learnt-so-far</guid><category><![CDATA[refactoring]]></category><category><![CDATA[Design]]></category><category><![CDATA[rewrite]]></category><dc:creator><![CDATA[Gagandeep Singh]]></dc:creator><pubDate>Sun, 24 Nov 2024 11:33:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/5Y8NrzPya-w/upload/2e2412c7cf9ffa4b16acbbf742b44ce2.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-background">Background</h1>
<blockquote>
<p>Refactoring is the process of restructuring code, without changing its original functionality.</p>
</blockquote>
<p>Over the past three years, my role has allowed me to pick a lot of refactoring tasks, which have enhanced my skills and understanding around <em>Refactoring</em>. Despite its proven benefits, many developers and teams approach refactoring with hesitation, weighed down by myths that it is <strong>risky, time-consuming, or equivalent to a complete rewrite</strong>. However, refactoring is none of these. When done correctly, it can significantly reduce technical debt, improve readability, enhance test coverage, and simplify debugging.</p>
<p>This blog aims to demystify refactoring by exploring its importance, addressing common misconceptions, and providing practical guidelines for approaching it the right way. Whether you’re tackling a legacy codebase or refining new code, understanding the essence of refactoring can empower you to build more sustainable software. Let's delve into <strong>why</strong> and <strong>how</strong> refactoring should be a regular part of your development process, not an intimidating task.</p>
<h1 id="heading-why-should-you-refactor-code">Why should you refactor code?</h1>
<p>After nearly a decade of writing software, I've learned that <strong>refactoring is unavoidable</strong>. While it's possible to delay it, putting it off only makes the task costlier and more complicated down the line. Ignoring refactoring may offer short-term relief, but in the long run, the cost of addressing the accumulated technical debt grows exponentially.. The below points should help you understand why you need to prioritize refactoring regularly:</p>
<h2 id="heading-to-untangle-code-ownership">To untangle Code Ownership</h2>
<p>One common complaint among developers working in a <em>fast-paced</em> startup environment is that the code for different modules often becomes tightly coupled, sometimes even within a single file. This situation arises because, in the beginning, the business logic is straightforward. Each time a developer needs to make a change, they perceive it as <em>just another simple change</em>. Over the years, this approach results in a file that resembles a <a target="_blank" href="https://en.wikipedia.org/wiki/God_object">god object</a>, which seems to know everything but has lost sight of its original purpose. Refactoring plays a crucial role in untangling this complex web of code. By doing so, it allows different teams to take ownership of specific files or modules. This can be effectively managed using tools like <a target="_blank" href="https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners">GitHub CodeOwners</a>, which help assign responsibility and maintain clarity in code management. Through refactoring, the codebase becomes more organized, enabling teams to work more efficiently and reducing the risk of errors when changes are made.</p>
<h2 id="heading-to-reduce-cost-of-debugging">To reduce cost of debugging</h2>
<p>Recently, I was on a call with another engineer, and we were trying to find problem in the production code. A section of the application wasn't functioning correctly, so we started by reading the code starting from the controller to trace the potential path a user might have taken. The controller file was cluttered, containing most of the business logic in one file. It was filled with numerous methods that called one another, each with lengthy lists of arguments that were difficult to guess. We spent over an hour trying to pinpoint the issue, but even after all that time, we weren't entirely confident about the root cause. Having dealt with debugging production issues in the past where the code was better organized, I realized that these engineers were losing countless hours every time a problem like this came. If you find yourself in a similar situation where valuable productive time is being wasted on debugging due to tangled code, it's crucial to have a conversation with your managers. Discuss the importance of planning for refactoring in the future. By doing so, you can improve the code structure, making it easier to identify and fix issues, ultimately saving time and resources in the long run.</p>
<h2 id="heading-to-reduce-techical-debt">To reduce techical debt</h2>
<blockquote>
<p>Technical debt is the cost of future rework that results from prioritizing speed over long-term design in software development.</p>
</blockquote>
<p>Companies often ship code that needs more work before adding new features. While launching the initial product is the top priority, addressing tech debt often gets delayed. For example, when working on a new module, you might hardcode values in an existing class. As more requirements come in, adding more hardcoded values or if-else statements just creates a mess. This means you need to refactor the code before building more on it. Remember this quote: <a target="_blank" href="https://news.ycombinator.com/item?id=33059910"><em>First make the change easy, then make the easy change</em></a>.</p>
<h2 id="heading-to-improve-engineering-culture">To improve engineering culture</h2>
<p>The cleaner and more well-structured your code is, the better it becomes as a reference for others. When you take the time to improve and maintain high-quality code, it not only benefits the project at hand but also sets a positive example for the rest of the team. New team members will be motivated by its clarity and organization, inspiring them to adopt similar practices in their own work. This fosters a culture of excellence, where everyone strives to write better code. On the flip side, if the codebase is cluttered and unorganised, there’s a high likelihood that others will follow that same pattern and create similarly messy code. It becomes a cycle, where poor practices multiply. As the team grows, this culture of quality spreads, impacting the overall productivity and the risk of technical debt based on the shape of initial code.</p>
<p>The habits you instill in your codebase today can shape the quality of work throughout the entire team tomorrow. Before we learn how to refactor, let's clear up some myths about refactoring.</p>
<h1 id="heading-myths-around-refactoring">Myths around refactoring</h1>
<h2 id="heading-its-another-name-for-rewriting">It’s another name for “Rewriting”</h2>
<p>Before we talk more about refactoring, it’s important to understand basic differences between <em>Complete Rewrite</em> and <em>Refactoring</em>:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Aspect</strong></td><td><strong>Complete Rewrite</strong></td><td><strong>Refactoring</strong></td></tr>
</thead>
<tbody>
<tr>
<td><strong>Scope</strong></td><td>Entire codebase is discarded and rebuilt.</td><td>Improves the existing code incrementally.</td></tr>
<tr>
<td><strong>Risk</strong></td><td>High risk of introducing new bugs or missing features.</td><td>Lower risk as existing functionality is preserved.</td></tr>
<tr>
<td><strong>Time &amp; Cost</strong></td><td>Time-consuming and often more expensive.</td><td>Typically faster and more cost-effective.</td></tr>
<tr>
<td><strong>Goal</strong></td><td>Start fresh with a new design or approach.</td><td>Improve code quality, maintainability, and performance.</td></tr>
<tr>
<td><strong>Disruption</strong></td><td>Can cause significant disruption to development and team workflow.</td><td>Less disruptive, as changes are small and incremental.</td></tr>
</tbody>
</table>
</div><p>I've seen developers (of all experience ranges) raising PRs with thousands of lines of changes across multiple files, claiming they've “tested everything thoroughly”. They think this one PR will solve all their problems, but that's rarely true. In most cases, the real issues begin after merging the PR. Don’t take this negatively if you’re following the same approach currently. Even I've been that developer who has merged large PRs without breaking anything on production, and I've seen others do it too, because they took the mental stress of testing every case and fixing all review comments on the large PR (and again test everything). The only problem with this approach is that <strong>it doesn’t scale</strong> for everyone in the team. While everyone can raise large PRs,</p>
<ol>
<li><p>Not everyone can <strong>test</strong> them thoroughly. And even if they do it once, they don’t do it with the same effort after fixing the review comments where they might have changed something in logic or in design.</p>
</li>
<li><p>Also, not everyone can <strong>review</strong> large PRs. As a result, many such large changes go live without a good code review. In my experience, reviewing large PRs can easily take more than an hour and not everyone is ready to put that much effort. These days I often don’t review large PRs. I spent ~ 5 minutes in understanding the structure of changed files and then leave a comment suggesting how the author can raise smaller PRs that are easy to review.</p>
</li>
</ol>
<p>Due to how developers have been approaching "refactoring", management and leadership have started seeing it in the same way as "rewriting." While raising a large PR for a new feature is safer (since most of the code is new), it is considered very risky for refactoring. If your code change breaks a stable module in production, it will confirm your manager's belief that <em>refactoring is as risky as rewriting</em>. So, make sure you avoid the “big bang” approach.</p>
<h2 id="heading-its-meant-for-legacy-applications">It’s meant for legacy applications</h2>
<p>Refactoring is linked to <strong>legacy systems</strong> because legacy systems often accumulate large technical debt over time, but it's just as important for <strong>new and evolving applications</strong>. Even in modern codebases, you can gather small debts like long classes/methods, duplicate code, unclear variable names, etc. Refactoring lets developers improve and optimize code continuously without a major overhaul. I often refactor code I wrote last week or last month. When a change makes a method or class messy, I find better ways to rewrite that code. It gives a different kind of satisfaction 😌.</p>
<h2 id="heading-its-risky-hard-and-unscoped">It’s risky, hard and unscoped</h2>
<p>The favorite thing for an engineer to do is build new features. The second favorite (for a lot of us) is doing a big-bang rewrite of an old system. But there are two issues with large rewrites:</p>
<ol>
<li><p>They have more chances of breaking production because of size of change.</p>
</li>
<li><p>They have less chances of getting shortlisted by your manager, again, because of it’s size. And because such tasks do not create any immediate business value, they gets shelved.</p>
</li>
</ol>
<p>If your manager has been a Senior IC person in the past, there might suggest you to split the project in order to get it prioritized. But if they are someone who’s not much into tech these days, and they do not suggest something like this, your refactoring project won’t get picked ever. So irrespective of how <strong>technical</strong> your manager is, it’s your role to understand the scope of the project and break it into parts that can be shipped. If you’ve never done it, please start doing it from now. Shipping new features can go in large PRs (although I don’t recommend you doing that) but refactoring projects should <strong>never</strong> go as large PRs. And because many developers have been executing them wrongly, refactoring is seen as <strong>risky, hard and unscoped</strong>.</p>
<h2 id="heading-its-a-one-time-event">It’s a one time event</h2>
<p>A significant amount of technical debt shows that your team hasn't focused on refactoring for a long time. If you compare the cost of refactoring a system today with the cost three years from now, you'll see that doing it today is easier and cheaper (unless the product is being retired in the next three years). This is because if experienced team members leave, new ones will struggle to understand the messy code that has built up over the years. Refactoring is a skill that everyone develops over time. Once you successfully complete a small refactoring project, you gain the confidence to handle more tasks and improve the system further. As you master this skill, it's important to share your knowledge with others, so everyone can help improve the existing codebase. Ideally, refactoring shouldn't be something you do only once or twice a year. It should be a <strong>continuous</strong> and <strong>intentional</strong> effort to keep technical debt from becoming too much to handle.</p>
<h2 id="heading-refactoring-is-moving-code-across-files">Refactoring is “moving code across files”</h2>
<blockquote>
<p>“Don't mistake motion for progress" - Alfred A. Montapert</p>
</blockquote>
<p>In web applications, there is a common principle that your controllers should remain small and focused. This means they should primarily call the service layer and handle the response based on the service's output. As a result, developers who find themselves writing extensive code within controllers might feel compelled to move that code elsewhere. While this instinct is understandable, it is crucial to approach this task with careful consideration and planning. Instead of simply <em>cutting and pasting lengthy methods</em> from one file to another, which only addresses the superficial goal of reducing controller size, a more thoughtful strategy is required. True refactoring involves a deeper analysis of each method's purpose and determining the most appropriate location for it. This process requires developers to think critically about the design and structure of their classes, ensuring that each method is placed in a context where it logically belongs. By engaging in a well-planned refactoring activity, developers can achieve more than just <em>smaller controllers</em>.</p>
<p>I believe you now have a solid understanding of what refactoring is—and what it isn’t. Now, let’s dive deeper into how to approach refactoring and explore the key challenges to watch out for during the process.</p>
<h1 id="heading-approaching-refactoring">Approaching Refactoring</h1>
<h2 id="heading-it-is-done-in-isolation">It is done in isolation</h2>
<p>When someone is working on a feature, they’re often motivated to fix the code around it. As a result, we get to see PRs where someone lints an entire file rather than the method they changed, or improved the logic of the methods that is getting called from their code. Their intention is to improve or optimise the code but the change could probably be taken up in a separate PR. Just like you don’t mix <strong>drinking</strong> with <strong>driving</strong>, avoid mixing <strong>feature changes</strong> with <strong>refactoring.</strong> Make sure that if you want to do the noble act of improving the existing code, raise a separate PR for it so that it can be individually tested, reviewed and released (and even reverted, incase something goes wrong). This will ensure that your <strong>feature changes</strong> do not get blocked due to review comments on this <strong>refactoring changes</strong>.</p>
<h2 id="heading-it-should-be-guided-by-tests">It should be guided by tests</h2>
<p>Test cases are incredibly important when you are refactoring any part of a system. They serve as a <strong>safety net</strong>, ensuring that the changes you make do not unexpectedly break an existing functionality. For example, if you have a comprehensive set of unit tests for a large file, these tests can guide you through the process of splitting the file into smaller, more manageable pieces. With unit tests in place, you can confidently refactor, knowing that any errors introduced during the process will be quickly identified. This allows you to focus on improving the code's structure and readability without worrying about unintended side effects.</p>
<h3 id="heading-what-if-i-dont-have-test-cases-for-some-code">What if I don't have test cases for some code?</h3>
<p>Incase you don’t have test cases, I recommend building a strong automated test suite first, or find ways to manually test the system to confirm everything works after refactoring. Please do not prioritize refactoring without a good way of testing out changes. I've been moving code from controllers to services, and from large services to smaller, more manageable services using this approach. I make sure my controller tests cover 100% of the controller and service code. Once that's done, I know that any changes I make will be caught if they break any part of the API contract.</p>
<h2 id="heading-refactoring-should-use-established-patterns">Refactoring should use established patterns</h2>
<p>While many believe that code is poetry — and I wholeheartedly support the idea of creative problem-solving, it's equally important to follow established patterns and best practices when refactoring code. These patterns have been developed and refined by the community over decades of experience while solving similar problems. They encapsulate lessons learned from solving common challenges, ensuring solutions are robust, efficient, and easier for others to understand. Established patterns, like <strong>design patterns</strong> (e.g., Singleton, Strategy, Factory), <strong>architectural styles</strong> (e.g., modular monoliths, microservices), solutions to <a target="_blank" href="https://refactoring.guru/refactoring/smells"><strong>common code smells</strong></a> (like long class, long methods, data clumps), and practices specific to a <strong>language</strong> or <strong>framework</strong> act as a shared vocabulary among developers. When you follow these patterns, you're not just solving a problem, you’re solving it in a way that others can immediately recognize, adapt, and build upon. This reduces cognitive overhead for team members, accelerates onboarding, and improves collaboration. While building a solution in way that it feels “uniquely yours” is a tempting feeling, these (overly creative) solutions can become difficult for others to maintain, debug, or extend over time. By grounding your refactoring efforts in proven patterns, you ensure the code remains accessible and future-proof.</p>
<h2 id="heading-avoid-premature-abstraction">Avoid Premature abstraction</h2>
<p>Abstraction is a great concept, but premature abstraction makes things complex. A <a target="_blank" href="https://en.wikipedia.org/wiki/Rule_of_three_\(computer_programming\)">common rule</a> in software engineering suggests to avoid having an abstraction till there are at least <strong>three repetitions</strong> of the code fragment that you’re planning to abstract out. While “three” might not be an appropriate number for everyone, it should be a good starting point for you if you don’t have anything else in mind. Waiting for a few repetitions of the code ensures that you have some data around what to generalize and what not to. While duplication of code is considered bad, generalizing prematurely might lead to speculative designs that are very different from the real world needs. Write code that is clear, direct, and solves the immediate problem effectively. Refactor and abstract <strong>only when</strong> the need becomes evident—when similar patterns emerge across different parts of the system, or when a particular piece of functionality needs to be reused or extended.</p>
<h2 id="heading-keep-changes-small-and-reversible">Keep changes small and reversible</h2>
<p>I think we’ve already discussed about the benefits of small PRs, so keeping the changes <strong>small</strong> is an obvious thing. Another important thing is to ensure that changes are <strong>reversible</strong>. As refactoring can touch very critical parts of your application, it is important to <strong>plan for failure</strong>. The simplest <em>rollback plan</em> would be to turn off the feature flag incase something goes wrong (or rollback the previous release). But that only works for deployments that didn’t modify any data. If you’ve modified some data in your database, it might be hard to rollback. As a precaution, try to follow an approach where rolling back data changes is also easy. For example, split your deployments like this:</p>
<ol>
<li><p>Add a new column that has new data. Push the new code along with this but don’t flip the feature flag yet.</p>
</li>
<li><p>Back populate the data in new column and add relevant callbacks in the system to ensure new data gets copied.</p>
</li>
<li><p>When previous data is also copied, flip the feature flag and see if everything is going well. If something goes wrong, immediately turn off the feature.</p>
</li>
<li><p>After few weeks/months (based on amount of testing you need), drop the old column, delete the old code and drop the if-else code handling the feature flag.</p>
</li>
</ol>
<p>Based on the scale of data and complexity of project, the above approach can sound normal or an overkill to you. But trust me, I’ve seen things going wrong just because developers didn’t plan well for rollbacks for <strong>data related changes.</strong></p>
<h2 id="heading-document-changes">Document changes</h2>
<p>Documenting changes during refactoring is essential for maintaining clarity, ensuring team alignment, and supporting future development. It captures the <strong>rationale</strong> behind the changes, helping others understand why specific decisions were made, especially if they impact dependencies or architectural patterns. Documentation preserves <strong>context</strong> to avoid undoing improvements and provides a historical record for long-term projects. It also simplifies onboarding by giving new team members a clear view of the code's evolution. Good documentation highlights <strong>what was refactored, why it was needed, and how it aligns with the project's goals</strong>, helping avoid confusion and duplication of effort. Including notes in commit messages, pull requests, or internal wikis ensures the refactoring's intent is communicated effectively. This practice not only fosters collaboration but also aids debugging by clarifying the purpose and impact of the changes, ensuring the refactored code remains accessible and adaptable.</p>
<h2 id="heading-monitor-performance-check-for-bugs">Monitor performance, check for bugs</h2>
<p>When refactoring, it's crucial to <strong>monitor performance</strong> and <strong>check for bugs</strong> to ensure stability and identify unintended side effects. While refactoring doesn’t guarantee performance improvements or fewer bugs, it can pave the way for these by simplifying the codebase and addressing inefficiencies. A well-refactored codebase should also make future optimizations (such as adding a caching layer) easier to plan and implement. Additionally, cleaner code helps in debugging and enhances maintainability (as already explained above). Use comprehensive test suites to validate changes and check for regressions and APM tools to monitor real-world performance after deployment.</p>
<h2 id="heading-value-contributions">Value contributions</h2>
<p>This message is for team leads and engineering managers: one of the most common reason why developers struggle with refactoring tasks is <strong>a lack of motivation</strong>. And often, this lack of motivation comes from how teams undervalue <strong>refactoring</strong> compared to <strong>business deliverables</strong>. As Eileen pointed out in her <a target="_blank" href="https://www.youtube.com/watch?v=olxoNDBp6Rg">latest RailsConf talk</a>, a good engineering culture starts at the top. Many of us have worked at organizations where messy code is shipped in the name of urgency, and the architecture becomes too complex to maintain over time. If we’re to take actionable insights from her experience, here’s what leadership can do to address these challenges:</p>
<ol>
<li><p><strong>Prioritize Refactoring -</strong> Treat refactoring tasks with the same importance as product development. Assign dedicated bandwidth for these efforts. Spending “few hours a day” don’t work and often lead to half-hearted results.</p>
</li>
<li><p><strong>Align Teams</strong> - Ensure engineers, product managers, and other stakeholders understand that refactoring is just as crucial as building features. A healthy codebase supports long-term business goals.</p>
</li>
<li><p><strong>Invest in Education -</strong> Not all team members naturally excel at refactoring. If specific skills are needed to address technical debt or architectural issues, invest time and resources to train your team.</p>
</li>
</ol>
<p>Eileen, with over a decade of experience at companies like Basecamp, GitHub, and Shopify, emphasized these points from her journey. While I haven’t worked at large organizations, I’ve faced similar challenges in small-to-medium startups. And seeing the comments on her talk, these problems persist even in teams following microservices architecture. So we can conclude that the challenges of maintaining a clean, scalable codebase are universal.</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>While refactoring is essential for maintaining a clean and efficient codebase, it's important to respect the existing code, even if it appears messy. This code was often written under tight deadlines to meet urgent business needs, and it has successfully supported the business for <strong>months and years</strong>. Recognizing the value of this "mess" is crucial, as it reflects quick thinking of developers who ensured the continuity and success of the business, which is crucial for any organisation.</p>
<p>Thank you for reading this post, have a good day 😊.</p>
<h1 id="heading-further-reading">Further reading</h1>
<ol>
<li><p>If you’re not yet thorough with design patterns and code smells, use this <a target="_blank" href="https://refactoring.guru/">free resource</a>.</p>
</li>
<li><p>Read <a target="_blank" href="https://newsletter.pragmaticengineer.com/p/paying-down-tech-debt">this post</a> by The Pragmatic Engineer around tech debt.</p>
</li>
<li><p>Find a book around refactoring for your specific programming language. For example, I read <a target="_blank" href="https://www.amazon.in/Refactoring-Ruby-Addison-Wesley-Professional/dp/0321984137">this book</a> for Ruby.</p>
</li>
<li><p>I already linked <a target="_blank" href="https://www.youtube.com/watch?v=olxoNDBp6Rg">this talk</a> in one of the points above, but adding this again incase you missed that!</p>
</li>
</ol>
<hr />
<p>If you liked this post, please read these too:</p>
<ol>
<li><p>Read <a target="_blank" href="https://blog.gagan93.me/11-conversations-with-10-software-engineers">this detailed blog</a> of my interaction with 10 engineers from different organisations.</p>
</li>
<li><p>If your team glamorizes overworking, please read <a target="_blank" href="https://blog.gagan93.me/eight-productive-hours"><strong>this</strong></a> (make them read too!)</p>
</li>
<li><p>Do not write code that does “a lot of things”, read <a target="_blank" href="https://blog.gagan93.me/problems-with-generic-code">this</a> to understand more.</p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[1:1 conversations with 10 Software Engineers]]></title><description><![CDATA[Background
I'm always looking for opportunities to engage with people who share similar interests and experiences. Recently, I shared a Google Form to connect with others, and I was thrilled to receive about 10 responses! Most of the engineers were b...]]></description><link>https://blog.gagan93.me/11-conversations-with-10-software-engineers</link><guid isPermaLink="true">https://blog.gagan93.me/11-conversations-with-10-software-engineers</guid><category><![CDATA[networking]]></category><category><![CDATA[engineering]]></category><category><![CDATA[Interviews]]></category><category><![CDATA[learning]]></category><category><![CDATA[growth]]></category><dc:creator><![CDATA[Gagandeep Singh]]></dc:creator><pubDate>Tue, 15 Oct 2024 08:17:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/HJmxky8Fvmo/upload/a07e6a62c88d79e0eef6bf036e6c6204.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-background">Background</h2>
<p>I'm always looking for opportunities to engage with people who share similar interests and experiences. Recently, I shared a <a target="_blank" href="https://www.linkedin.com/feed/update/urn:li:activity:7236405029618774016/">Google Form</a> to connect with others, and I was thrilled to receive about 10 responses! Most of the engineers were based in India, with one from Switzerland. It was great to chat about their work, challenges, and career growth, and I learned a lot from these conversations.</p>
<p>After some of these discussions, I posted a few <a target="_blank" href="https://www.linkedin.com/feed/update/urn:li:activity:7243656624869949440/">interesting insights</a> from our calls. I’m now writing this (detailed) post to share some highlights and reflections, as many of the conversations included requests for advice that could benefit a wider audience.</p>
<h2 id="heading-what-all-we-discussed">What all we discussed?</h2>
<p>In this section, I am sharing all the key points that we discussed during our calls. Each of these points will be explored in detail in the sections that follow. Most of the engineers had 4-7 years of experience and wanted advice on career growth along with potential future work and challenges. Here's what we talked about:</p>
<ol>
<li><p><strong>Why am I doing this for free? Do I offer paid mentorship? -</strong> Not everyone asked this but two separate people asked each of these questions.</p>
</li>
<li><p><strong>How to prepare for Interviews?</strong> - Half of them were either in their notice period or actively searching for new opportunities. I was able to provide detailed insights on this topic, having recently gone through a job transition myself. Additionally, I shared thoughts on various learning resources because all available content is not of good quality.</p>
</li>
<li><p><strong>How do large teams work?</strong> - It's fascinating to observe how massive products like YouTube, Amazon S3, or Uber are developed and maintained. These platforms are supported by extensive teams that collaborate across different regions and time zones. Understanding the dynamics of such large teams can provide valuable insights into how complex projects are managed and delivered successfully.</p>
</li>
<li><p><strong>How do I hire people?</strong> - I’ve given interviews at about 6-8 companies before switching this time. But at LocoNav, I had interviewed &gt; 200 people for various engineering roles including Senior Engineers, Managers, QA engineers, Devops. So I shared some insights here based on my experience.</p>
</li>
<li><p><strong>What are cultural differences between Indian and non-Indian teams?</strong> - I discussed this with someone working outside India who also had experience working in India. Many others have shared similar insights, especially those who have dealt with "not so good" bosses.</p>
</li>
<li><p><strong>How do you handle/convey delays across layers of stakeholders?</strong> - Handling this can be challenging, especially when it involves end customers. We'll discuss a framework that helps keep everyone satisfied.</p>
</li>
<li><p><strong>Are Big Tech engineers are superior to us?</strong> - Someone had this in their mind, so had to discuss around this inferiority complex. Another guy had a question - <em>Why my friends in big tech companies get paid so much if they do same work as we do?</em></p>
</li>
<li><p><strong>How to grow as an Engineer (not a Java Engineer or Ruby Engineer)</strong> - It's important to be skilled in at least one language, but it's even more important to stay flexible and ready to learn anything new that comes your way.</p>
</li>
<li><p><strong>How to know about scale without working on scale</strong>? - Not everyone gets a chance to work on scale or work on certain things like Devops, but many companies seek that experience. How to counter that?</p>
</li>
<li><p><strong>How I generally approach learning anything new?</strong> - I was asked this question in an interview also. I’ll also share what I call as <strong>continuous learning</strong>.</p>
</li>
<li><p><strong>How often your managers sync and how does it impact your growth?</strong> - I’ve been managed by all sorts of people — from those who didn’t sync in months to the current org where managers mostly sync weekly.</p>
</li>
<li><p><strong>How to know about team’s culture before joining?</strong> - While salary details are clear before you join, it's tough to understand a company's culture until you start working there. However, there's a simple trick you can use to learn about the company before accepting an offer.</p>
</li>
<li><p><strong>How to be good at real world HLD?</strong> - As I spoke with mid-level engineers aiming to become senior engineers, they were eager to learn about real-world system design. In many companies, you rarely get the chance to design large systems because high-level designs are costly to redo, and most people work on systems that are already designed. Despite this, it's crucial to understand core system design concepts so that even when making small changes to an existing design, you are aware of their implications.</p>
</li>
<li><p><strong>I’m not working on Kubernetes or Microservices, will I be easily able to switch? -</strong> While I believe that it’s not important to get your hands dirty in every single hot technology to be relevant in the current market, you must remain updated with a few things.</p>
</li>
</ol>
<p>Let’s discuss about each of these one by one. This is going to be a long blog post because it’s summary of more than 13 hours of discussion. If you find this really long or parts of it not relevant to you, feel free to read specific parts using table of contents on the top.</p>
<h2 id="heading-why-am-i-doing-this-for-free-do-i-offer-paid-mentorship"><strong>Why am I doing this for free? Do I offer paid mentorship?</strong></h2>
<p>Many engineers, especially those at Big Tech companies, offer paid mentorships through platforms like Topmate, charging varying fees. While having a second income is fine, I've received help throughout my 9-year career without being charged. It doesn't feel right to start charging for sharing the same knowledge. My religious beliefs also motivate me to offer help for free. I've encouraged others to help for free whenever possible. My job pays well, so I don't want to charge for an hour of conversation. Additionally, not all calls are about mentorship; some people just want honest chats about work, experiences, productivity, team culture, and more. As of now, I don't plan to offer paid mentorship services. Feel free to message me on LinkedIn if there's anything I can help with.</p>
<h2 id="heading-how-to-prepare-for-interviews">How to prepare for Interviews?</h2>
<p>As half of the engineers were in their notice period, this was a pretty common question — How to prepare for interviews and what all things to study. I’ve already written in detail about my <a target="_blank" href="https://blog.gagan93.me/tag/interview">interview experiences</a>, <a target="_blank" href="https://blog.gagan93.me/improving-your-resume">resume improvement journey</a>, <a target="_blank" href="https://blog.gagan93.me/low-level-design-interviews">low level design interviews</a> and <a target="_blank" href="https://blog.gagan93.me/titles-inflation-and-down-levelling">job titles</a>. I'll still share more based on their specific questions.</p>
<ol>
<li><p><strong>Learn about different companies</strong> <strong>and target accordingly</strong> - Talking specifically about engineering roles, there are different types of companies like services companies, consultant companies, early/mid-age startups (Seed/Series-A/Series-B), Scale-ups (Series C+, post IPO), and Big-Tech companies. To explain a bit about each of them:</p>
<ul>
<li><p><strong>Services companies</strong> typically include (but not limited to) Infosys, TCS, Accenture, Cognizant etc.</p>
</li>
<li><p><strong>Consultant</strong> companies could include <a target="_blank" href="https://en.wikipedia.org/wiki/Big_Three_\(management_consultancies\)">MBB</a>, Thoughtworks, etc. Similar to the first one, you are working for others (not on your own products) but the role consultant-based where you completely handles client, not just code.</p>
</li>
<li><p><strong>Early/mid age startups</strong> could include any company that is going 0-1 (building MVP) or 1-10 (secured some funding and building for real customers). People in such companies are expected to be <strong>generalists</strong>, who can wear multiple hats and can get work done really fast.</p>
</li>
<li><p><strong>Scale ups</strong> could include public listed companies. I could categorize them as young, agile, fast-moving but having lot of governance and processes because of being a public entity.</p>
</li>
<li><p><strong>Big Tech</strong> (as you’d expect) includes FAANG, and similar sized companies like Atlassian, Uber, Stripe etc. I’d call any other company as BigTech if they have &gt;1000 engineers. In comparison to any other kind of company, here you’d find a lot of internal tooling.</p>
</li>
</ul>
</li>
</ol>
<p>        There's a trend of people moving from services companies and startups to Big Tech companies, but I've also seen people move from startups to other startups or from Big Tech to startups. For example, I started at a services company (not as big as the ones mentioned) and then moved to startups. So I’d recommend you to decide types of companies you want to target and prepare for them accordingly. There are many websites, videos, and interview experience blogs that explain the interview processes for each of these.</p>
<ol start="2">
<li><p><strong>Process familiarity</strong> - All companies have their own process for selecting candidates. While common rounds like DSA, LLD, HLD, and HM exist in most companies, always get the details from your recruiter. If you're interviewing with a large company, the recruiter will likely give you a document explaining the process with resources to help you prepare. In smaller companies, you often need to ask questions to get clarifications. I've met engineers who didn't know the total number of rounds because the recruiter didn't explain, and they didn't ask. You must ask these questions to understand what is expected in each round so you can prepare accordingly.</p>
</li>
<li><p><strong>Resources</strong> <strong>for preparation</strong> - A lot of my work over the past 2-3 years has focused on refactoring and migrating systems, which has improved my low-level design skills. One way to prepare is by taking on similar work in your current role to naturally enhance your design skills. However, this approach doesn't work for DSA and HLD rounds. That's why I view the LLD round differently from the DSA/HLD rounds. Generally, people in roles like SE, SSE, or Staff, who are individual contributors, are involved in building or maintaining systems, writing, and deleting a lot of code. Engaging in similar work will automatically improve your low-level design skills as you create and refine system designs. On the other hand, most of us don't use the advanced data structures that are tested in interviews, making DSA a topic specifically for interview preparation. Similarly, few engineers get the opportunity to build or modify high-level designs, so they don't improve practically in System Design. Working at an early-stage startup in my last role gave me a solid understanding of cloud and system design, which many engineers don't get. To succeed in these rounds, I recommend spending a few weeks or months (depending on your current skills and preparation time) to master the basics of these areas:</p>
<ol>
<li><p><strong>Resume building</strong> - <strong>Please please please</strong> spend time in building and continuously improving your resume. Eliminate all spelling mistakes, punctuation errors, and any other beginner mistakes. For a detailed overview on how I did it, read <a target="_blank" href="https://blog.gagan93.me/improving-your-resume">this blog</a>.</p>
</li>
<li><p><strong>DSA</strong> - Leetcode helped me improve my DSA skills, but for my role (Senior/Staff) the questions were mostly easy-medium level. Based on your level and companies you’re targeting, you might be asked a combination of easy/medium/hard questions, so prepare accordingly.</p>
</li>
<li><p><strong>LLD</strong> - I studied from multiple youtube channels and solved some problems in Ruby and Java (available <a target="_blank" href="https://github.com/gagan93jtg/lld-learning">here</a>) so that I can solve them on a decent speed during the interview. I did not spend a single penny on a paid resource for LLD. If you want to know more about different kinds of LLD interviews I gave, I’ve written a <a target="_blank" href="https://blog.gagan93.me/low-level-design-interviews">detailed blog</a> on it already. Feel free to check it out.</p>
</li>
<li><p><strong>HLD</strong> - I was skeptical on my HLD skills so I took a paid course but that was badly structured. It was enough to confuse any beginner but having a good idea of those concepts already, I was able to navigate through it and complete about 70% of it before giving interviews. Later, someone told me about Educative.io and it looked much better organised. I’m not pushing you to use the same resource because of a few reasons. First of all, it’s a paid course and I do not promote anything paid on my blogs. Secondly, it’s a text based course while most of the other courses are video based. I’m fond of reading so I purchased it, you may not like it if you’re not into reading. And third, their well-known system design course (Grokking) is very long. So I’d recommend this only if you want to learn the concepts beyond interviews and the course falls within your budget. Although I don’t endorse it, I can vouch for the quality of content.</p>
</li>
<li><p><strong>Behavioural / Hiring manager</strong> - Watch some free youtube videos to understand the structure of this round and what people generally ask. Beyond that, just be true to yourself and be good at communicating your thoughts. You might struggle here if you’re not an efficient communicator.</p>
</li>
</ol>
</li>
<li><p><strong>Where to apply? -</strong> There are plenty of websites could help you. Again, I didn’t spend anything for my job search but these are the websites I used - LinkedIn jobs (set relevant job alerts), Tophire (I got Branch’s offer through them), Bigshyft (I still get calls from them 😂), Instahyre (Atlassian reached out through this) and Hirist. I also registered on Indeed and Naukri but never got relevant openings through them. You can find and register on more platforms but at least register yourself on these.</p>
<p> I hope this helps for interview preparation. Moving on to the next topic 😎.</p>
</li>
</ol>
<h2 id="heading-how-do-large-teams-work">How do large teams work?</h2>
<p>Let me add more context to this question — This was asked by someone who was working for a startup for about 4 years, and was wondering how things would unfold if their team scaled 2x-3x in strength. How would the projects be managed and what challenges would exist for them being an old member of the team who is transitioning more towards a managerial role. This is a common question for people working in small teams. They sometimes wonder how large teams operate to make something as large as Youtube, Google Maps, or Amazon S3.</p>
<p>I’ve been in a habit of reading a lot of content in the past few years from which I shared the following insights:</p>
<ol>
<li><p>First of all, no large system is built in a day, or week, or even a month. Smart engineers start building things in the most stupid way, and iterate on them as they find bottlenecks in the system that prevents it from scaling further, so that they can replace those specific parts of the system. I remember my last company’s co-founder (who was also a Ruby engineer) using a hacky way for partitioning tables in Postgres 9.3. If you’ve used that version of Postgres vs Postgres 12+, you’d know that the current partitioning features didn’t even exist in that version. But at that time we didn’t have enough choices because we were pretty early stage and had limited exposure to good tech. Once it was no longer scalable for us and the cost became very high, the team handling that system redesigned the same into a separate data layer that used a suitable technologies. It’s very important to start stupid initially and satisfy the business needs so that you can sell whatever you made. For example, it would be hard to assume that YouTube used MySQL to begin with, and they made it work till a <a target="_blank" href="https://www.reddit.com/r/programming/comments/1d4u12d/how_youtube_was_able_to_support_249_billion_users/">massive scale</a>.</p>
</li>
<li><p>Having large systems doesn’t mean that you have really large teams that run them in a monolith fashion. For example, Amazon’s <a target="_blank" href="https://aws.amazon.com/executive-insights/content/amazon-two-pizza-team/">two pizza rule</a> is very popular in the software industry that says — <em>No team should be big enough that it would take more than two pizzas to feed them</em>. <em>Ideally, this is a team of less than 10 people: smaller teams minimize lines of communication and decrease overhead of bureaucracy and decision-making.</em> Also, because we’re talking about large teams and about Amazon, <a target="_blank" href="https://highscalability.com/behind-aws-s3s-massive-scale/">this article</a> is a great reference. The most popular blob storage service, Amazon S3 alone is composed of more than 300 micro-services, and you cannot assume all the teams building them to come in one giant standup thrice a week to talk about their work. It’s obvious that a lot of engineers and managers would be managing these services so that they can focus on specific areas. At the same time, it’s very easy to talk about good engineering management at that scale, than doing it efficiently. So I always appreciate how these large organisations remain agile and build massive applications for their customers.</p>
</li>
<li><p>If your company is building really large systems, they also invest in building Internal tools so that developers in different teams follow same standards and do not reinvent the wheel for solving similar problems. In my previous role, I’ve worked in the developer productivity team for more than one year. As our team was scaling in strength and developers were breaking the system into microservices, we were building internal tools for configuration management and deployment so that we could easily deploy the applications to EC2 machines, dockers or kubernetes. We were required to support multiple deployment systems because we had not transitioned fully to kubernetes and different applications (both old and new) were getting deployed to different platforms.</p>
</li>
<li><p>Not all the processes of large organisations make sense for small organisations, or for those organisations that are scaling to a larger one but I’d like to emphasize on one very important rule that impacts the success of projects — <a target="_blank" href="https://daily.stoa.com/newsletter/single-threaded-leadership">Amazon’s Single threaded leadership principle</a>. In startups, we generally don’t hire people as new initiatives begin. As a result, same set of people are juggling (and sometimes struggling) with multiple initiatives. If there are some gaps in the quality of delivered products, it’s acceptable to the leadership because they know that people are trying their best. With teams scaling to a larger strength, it is important to revisit this culture. If it is not acceptable going forward, it’s important to improve the team’s culture in a way where people are not overwhelmed with projects and whatever they deliver is of much better quality. The <strong>Single Threaded Leadership</strong> <strong>Principle</strong> might make some sense for the scaling organisation because at a specific point, teams might be catering enterprise clients where quality is very important.</p>
</li>
</ol>
<h2 id="heading-how-do-i-hire-people">How do I hire people?</h2>
<p>I’ve been contributing to hiring efforts from my second year as a Software Engineer, where I use to take DSA round. I’ve taken hundreds of interviews since that time, which include the same for Engineers (junior / senior / lead), Managers, QAs and Devops. I personally believe that it’s very important to understand the candidate and their aspirations, rather than just throwing a problem in front of them and talking about it for an hour. Interviews of a candidate should happen in a way where each interview helps you to paint a clearer picture of their behaviour and skills rather than every round starting with the old “please introduce yourself” question. To hire anyone in future, I’d look for these things:</p>
<ol>
<li><p><strong>Technical excellence</strong> - If it’s a hands-on technical role, it’s very important to ensure that the candidate understands code and should be fluently coding in one language of their choice. I always focus on the basics rather than evaluating someone on a fancy leetcode problem. As an example — while interviewing Devops people, my favorite interview question use to be <em>“You’re trying to SSH to a machine but the command is unresponsive unless you cancel it. How would you debug this?”.</em> I want to see how people respond to such basic questions —What clarifications do they take on this small question? Do they talk about network barriers like security groups or VPC route tables? Do they consider the fact that if the authentication key is wrong, then you immediately get an error rather than having a hung connection. I appreciate good debugging skills.</p>
</li>
<li><p><strong>Ability to break a problem</strong> - You could learn about this while evaluating design rounds, or when they explain some of their projects. IMO, if someone can break problems into parts, they can work on things incrementally rather than doing big-bang releases/refactors. Also, these people estimate projects better than those who do not understand the <a target="_blank" href="https://blog.gagan93.me/classify-your-codebase">power of small changes</a>. This is an under-rated technical skill.</p>
</li>
<li><p><strong>Programming language agnostic</strong> - I’ve done one wrong hiring in the past where the candidate was too much tied with Spring boot framework (not just Java). They told about the same after joining but I was not managing projects that were Java based. In the end I had to move them in some other team because the other option was to let them go. As a learning, I’d not expect a person to be <a target="_blank" href="https://www.techtarget.com/searchsoftwarequality/definition/polyglot-programming">polyglot</a> already, but open enough to work in any language.</p>
</li>
<li><p><strong>Quality of work -</strong> When evaluating their solution in any round, check if their solution meets the ask of the problem statement. How well do they handle the edge cases? Do they ask enough clarifications where required (or they just assumed those)?</p>
</li>
<li><p><strong>Getting things done -</strong> While quality of work matters, I’ve seen people getting stuck on small things and not having enough bias for action. Having worked in startups, I think this is something I’d look for all kinds of roles. If you’re a good engineer who writes optimised code but do you not push the barriers that come your way, you might not be able to release something real that gets used by the customers.</p>
</li>
<li><p><strong>Cultural aspects</strong> - In small organizations, while we may not conduct formal behavioral interviews, all interviewers are vigilant about observing a candidate's behavior throughout the interview process. This careful observation is crucial to ensure that we do not end up hiring someone who might not be a good fit for the team. From my experience, I have witnessed the consequences of hiring the wrong person, ranging from fresh graduates to Heads of Engineering (HOEs). Depending on their role and influence within the team, a poor hiring decision can lead to significant negative impacts, even within a short timeframe.</p>
</li>
</ol>
<p>To summarize:</p>
<ol>
<li><p>Good basics + great debugging skills.</p>
</li>
<li><p>Ability to break problems and some past experience working on quality projects.</p>
</li>
<li><p>Bias for action, humble and eager to learn.</p>
</li>
</ol>
<h2 id="heading-what-are-cultural-differences-between-indian-and-non-indian-teams">What are cultural differences between Indian and non-Indian teams?</h2>
<p>I think all of you have heard of a few cases in the recent past where employees died at work or committed suicide due to work pressure. It’s very unfortunate to see the same companies having great working culture outside India and highly toxic culture in India. I wrote about <a target="_blank" href="https://blog.gagan93.me/blameless-culture">blameless culture</a> recently where I shared some examples of how good or bad culture propagates from the top to bottom, or from old employees to new employees. It all boils down to a few people on the top that we call as <strong>the leadership.</strong> These people define:</p>
<ol>
<li><p>Code values and principles.</p>
</li>
<li><p>Quality of work.</p>
</li>
<li><p>Communication Style.</p>
</li>
<li><p>Response to mistakes / failures.</p>
</li>
<li><p>And most importantly - Work life balance.</p>
</li>
</ol>
<p>For me, the 16-hour workday philosophy is nonsense. If I don't have time to think about other things in life, I can't grow personally or professionally. I've worked with US teams in my first organization, and now I work with both US and Africa teams (in addition to the India team). Fortunately, the overall culture encourages taking leave whenever needed, whether for a personal emergency, rest, or vacation. People in the US don't work beyond a certain limit, but that doesn't mean they haven't created good products or scaled companies.</p>
<p>In a nutshell,</p>
<ol>
<li><p>Do not over-work regularly. Doing the same for a few days should be fine based on the business needs and your physical/mental health.</p>
</li>
<li><p>If you or your team overworks everyday, figure out what could be wrong (some ideas <a target="_blank" href="https://blog.gagan93.me/eight-productive-hours">here</a> and <a target="_blank" href="https://blog.gagan93.me/efficient-fullstack-delivery">here</a> for engineering teams).</p>
</li>
<li><p>Do not value any work or opportunity above your health or family.</p>
</li>
<li><p>Fix the company culture if something is wrong. If the leaders do not support you, find a new job.</p>
</li>
<li><p>Take your work seriously. In the 8-9 hrs you put, give your 100%.</p>
</li>
</ol>
<h2 id="heading-how-do-you-handleconvey-delays-across-layers-of-stakeholders">How do you handle/convey delays across layers of stakeholders?</h2>
<p>Let's consider this hierarchy: developers (including you) → project manager → business teams → customer. Anyone who has successfully launched a product knows that initial estimates are often inaccurate. The accuracy of your estimates depends on many factors, such as:</p>
<ol>
<li><p>How <strong>undefined</strong> is the problem? - Do we need to check if it's feasible? Are there dependencies on other teams?</p>
</li>
<li><p>How much <strong>experience</strong> does our team have in building something like this? - This includes the experience of senior engineers, product managers, and designers.</p>
</li>
<li><p>How well did we <strong>break down the problem</strong> while estimating? - If you break it down well, you'll likely complete half of the software design, and your estimates will be closer to accurate.</p>
</li>
<li><p>How <strong>new</strong> is the team? - If there are many new members, there is a possibility of inaccurately estimating their speed in developing production-ready systems.</p>
</li>
</ol>
<p>Beyond this, each layer should understand that the previous layers might exceed their estimates, so they should include their own buffers. For example, if developers estimate 6 weeks, managers should say 7-8 weeks, and the business team should assume 8-10 weeks. It's okay to <em>under-commit and over-deliver</em> (but not the reverse).</p>
<p>If your team can follow this kind of framework, that's excellent. It means you won't have to struggle as much to convince customers who have been waiting for a feature. A note for managers: your team might sometimes struggle with accurate estimations. However, that doesn’t mean you should provide your own estimates to the business team. Non-engineers should avoid estimating on behalf of engineers. If you find that the team's estimates often miss the mark, take the time to discuss and reflect with them.</p>
<h2 id="heading-are-big-tech-engineers-are-superior-to-us">Are Big Tech engineers are superior to us?</h2>
<p>I respect great engineers and managers who have built really large systems without which the world would have been very different. At the same time, do not think that they are of a different breed. I think engineers in startups typically work in environments with fewer resources, less structure, and more uncertainty. They wear multiple hats, tackling everything from backend to frontend, cloud and even take product decisions. This exposure forces them to develop a holistic understanding of technology and the business. In contrast, engineers at Big Tech firms may specialize in a narrow domain within a larger system, where their role and responsibilities are more clearly defined. While specialization can lead to deep expertise in specific areas, it may also mean less exposure to the end-to-end development process, product strategy, or direct customer interaction.</p>
<p>The perception of Big Tech engineers being superior is often tied to the prestige of working at a well-known company and the assumption that these engineers have passed rigorous technical interviews. Another key difference is the sense of ownership and impact. In startups, engineers are closer to the product and customers. The features they build, the bugs they fix, and the architectural decisions they make have a direct influence on the company's trajectory. This proximity to the business side creates a different kind of pressure but also provides unique learning opportunities that many Big Tech engineers may not experience. Ultimately, the skills and experiences gained in startups can be just as valuable as those acquired in Big Tech. Both environments have their strengths, and neither is inherently superior to the other. So the engineers in startups shouldn’t view themselves as "inferior" simply because they don't work for a large company.</p>
<p>On the other hand, big tech companies manage vast products, extensive business operations, and significant overall revenue. In such environments, even small optimizations made by engineers can lead to substantial financial benefits. For instance, a minor improvement in code efficiency or system performance can save millions of dollars due to the scale at which these companies operate. Therefore, it's important not to assume that engineers in big tech are performing the same tasks as those working on an app serving only 1,000 users. The scale, complexity, and potential impact of their work are vastly different. Engineers in big tech often deal with challenges related to scalability, security, and global user bases, which require a different set of skills and approaches compared to those needed in smaller-scale applications.</p>
<h2 id="heading-how-to-grow-as-an-engineer-not-a-java-engineer-or-ruby-engineer"><strong>How to grow as an Engineer (not a Java Engineer or Ruby Engineer)</strong></h2>
<p>I’ve been fortunate to be mentored by someone who was a polyglot programmer. Although I’ve majorly written only Ruby in production throughout my career, I’m mentally aligned to think about problems without tying them with language specific constructs. Knowing multiple languages help you to apply good things of one language in another language and build something in a unique way. With this knowledge, you can also plan to build certain parts of a system in a different language that is more optimised for the use case, especially when you get a chance to split them into services.</p>
<p>In many of these discussions that I had with these engineers, I told them to start thinking of solving problems without talking about the language. Also, whenever given a chance, do not shy away from learning a new programming language. The maturity that comes with this mindset also helps you to write build documentations where you use more generic / industry standard terms rather than using the names of specific libraries that were used to implement the same (eg. calling part of system as <strong>background job processor</strong> rather than <a target="_blank" href="https://github.com/celery/celery">celery</a>, <a target="_blank" href="https://www.jobrunr.io/en/">jobrunnr</a> or <a target="_blank" href="https://github.com/sidekiq/sidekiq/">sidekiq</a>).</p>
<h2 id="heading-how-to-know-about-scale-without-working-on-scale">How to know about scale without working on scale?</h2>
<p>This question came from someone who was asked about database query optimisation in an interview. While they knew about indexes and a few other things that help you optimise queries, they had never done something like this in the past. I think there are two ways to learn about scale:</p>
<ol>
<li><p>Build something (in a company or as a personal project) that scales to millions of users.</p>
</li>
<li><p>Or, see how others did it.</p>
</li>
</ol>
<p>By "others," I mean learning from the experiences and mistakes of other people. Almost all large tech companies have blogs where they publish case studies, failure stories, and success stories. In addition, there are independent bloggers who write about their experiences in building and debugging large systems. Subscribe to some of these blogs, and you'll notice a difference in your knowledge in the next three months.</p>
<p>If you’re not sure where to start from, I’ll give you some ideas:</p>
<ol>
<li><p>Think of the big tech companies that your admire (examples - <a target="_blank" href="https://github.blog/engineering/">Github</a>, <a target="_blank" href="https://engineering.atspotify.com/">Shopify</a>, <a target="_blank" href="https://stripe.com/blog/engineering">Stripe</a>, <a target="_blank" href="https://instagram-engineering.com/">Instagram</a>, <a target="_blank" href="https://slack.engineering/">Slack</a>).</p>
</li>
<li><p>Follow some good substack newsletters. I really like <a target="_blank" href="https://www.pragmaticengineer.com/">Pragmatic engineer’s</a> newsletter (although a lot of content is paid).</p>
</li>
<li><p>A lot of people reading this might already be familiar with <a target="_blank" href="https://www.youtube.com/@AsliEngineering">Arpit’s youtube</a> channel. I really admire the quality of this content.</p>
</li>
<li><p>You could consider subscribing to <a target="_blank" href="https://blog.gagan93.me/newsletter">my blog</a> if you find the content good enough 🫣.</p>
</li>
</ol>
<h2 id="heading-how-i-generally-approach-learning-anything-new"><strong>How I generally approach learning anything new?</strong></h2>
<p>There are generally two kinds of things that anyone is learning:</p>
<ol>
<li><p>If you’re learning something to quickly build a prototype where you do not care about quality, use any source to learn. It could be a 10 minute youtube video, or a brief documentation, or ChatGPT prompts, that help you write some code, or understand a few basic concepts about a new technology.</p>
</li>
<li><p>If you’re serious about learning that topic, still follow step 1 so that you build some interest and then <strong>learn from a book</strong>. I’m old an school guy here, who would prefer books over videos. I think these days, anyone can put their camera on a tripod stand to record a video after a little bit of research. Here the quality of content might be good, average, or bad. But if you’re picking a book of a good publisher, the chances of getting a good quality content are much higher. This is my own theory, feel free to disagree 😂.</p>
</li>
</ol>
<p>I learnt docker back in 2022 using this method. Earlier I just created a Dockerfile for one microservice that I was building. It wasn’t optimised at all (I copied it from somewhere). Later I read <a target="_blank" href="https://www.manning.com/books/learn-docker-in-a-month-of-lunches">DIAMOL</a> to get a deeper understanding of Docker, after which I did a lot of optimisations in different Dockerfiles and wrote this <a target="_blank" href="https://blog.gagan93.me/optimising-docker-builds">detailed article</a> to help the community.</p>
<p><strong>Continuous learning</strong> - Your work might be very challenging, but it can still limit your learning to certain areas. Make sure to set aside some time regularly (weekly or monthly, if not daily) for learning. I understand that not every day is the same. Some days are more stressful, while others are more relaxed. Create a list of blogs you want to read or videos you want to watch, and learn from them whenever you have some free time.</p>
<h2 id="heading-how-often-your-managers-sync-and-how-does-it-impact-your-growth">How often your managers sync and how does it impact your growth?</h2>
<p>In my first organization, there was no formal manager hierarchy in my team. We all reported to one of the three founders. Our project was in maintenance mode, so we didn't have a senior resource like a Tech Lead assigned, as they were in other teams. I wouldn't say my growth was halted because of this, but now that I have managers who sync weekly, I see the benefits of regular manager check-ins. Many people in these calls mentioned their managers don't sync with them regularly. Here, <em>regular</em> doesn't mean weekly but rather on a fixed schedule. This depends on factors like:</p>
<ol>
<li><p>The company culture might not encourage managers to have regular meetings with all their direct reports.</p>
</li>
<li><p>Your manager might be on a technical role like tech lead and not a “manager-only” role where they have a tight schedule with their own deliverables.</p>
</li>
<li><p>You might be part of a fast-paced team where these meetings aren't prioritized because delivering products is the main focus. The team may be learning a lot during this process, but that doesn't ensure your goals align with the company's goals.</p>
</li>
</ol>
<p>In my previous role, I met with my manager once every 2-3 months, and I was managing a team of 7 people myself. Since I hadn't experienced regular meetings with my managers before and wondered, "What would we even discuss every week?" I didn't schedule regular meetings with my team. Instead, I told them to reach out to me at least once a month if they felt the need. In my current role, all managers meet with their direct reports weekly. They discuss everything from professional to personal topics to help you feel comfortable and aligned for growth. In short, managers should set up regular meetings (weekly, every two weeks, or at most monthly) to ensure that:</p>
<ol>
<li><p>Their direct reports do not feel disconnected from them, especially in a remote setup. A strong connection is important to ensure that employees are motivated and feel comfortable enough to give feedback for the team's overall improvement.</p>
</li>
<li><p>If there are performance issues or expectation gaps, they should be addressed early, rather than during the appraisal cycle. This ensures you have enough time to fix those gaps promptly.</p>
</li>
<li><p>Your career goals are aligned with the company's goals.</p>
</li>
<li><p>These meetings can also cover topics beyond work, like how you are planning an extended weekend or how everyone is doing at home. After all, a healthy team is like a family :)</p>
</li>
</ol>
<h2 id="heading-how-do-i-know-about-teams-culture-before-joining"><strong>How do I know about team’s culture before joining?</strong></h2>
<p>A strong team culture fosters productivity by creating a supportive environment, while a toxic culture can decrease morale, leading to disengagement, lack of collaboration, and ultimately, low productivity. More than compensation, culture is crucial, yet many people overlook it before joining a company. If you've only experienced companies with good culture, you might not recognize bad culture till you get to see it first-hand. It's important to know:</p>
<ol>
<li><p>If the team engages in productive meetings or they just setup long meaningless meetings with a lot of folks. Do meetings have agenda and end with either action items or followup meetings?</p>
</li>
<li><p>How is failure handled? Does the team engage in blame game or fixes the root cause and learns from it?</p>
</li>
<li><p>How do people generally feel about the product they’re working on? Are they proud of it?</p>
</li>
<li><p>Do people encourage sharing feedbacks openly or does everyone think “they’re right”?</p>
</li>
</ol>
<p>At this point, you might be thinking — how do I know about these things before joining? It’s not that hard. I’d do all this to confirm the same:</p>
<ol>
<li><p>During your interviews, pay attention to how organized and punctual the interviewers are. Notice their communication style, how they evaluate your solutions, and the feedback they provide. A big red flag for me in design rounds is when people have fixed solutions in mind and <strong>force</strong> you to change your design to match theirs. Such people won't let you innovate even after you join the team.</p>
</li>
<li><p>Check online reviews on some websites to get a sense of the company. Remember, some reviews can be biased, so this should <strong>not be the only thing</strong> you consider.</p>
</li>
<li><p>Talk to current or former employees to see how they feel about their work. Ask specific questions (like those listed above) instead of just asking, "How do you feel working at &lt;company_name&gt;?" Make sure you <a target="_blank" href="https://blog.gagan93.me/asking-good-questions">ask good questions</a>. If they're unhappy, they likely won't want someone else to join and be unhappy too. To get a better idea, talk to at least two people.</p>
</li>
<li><p>In discussions with your recruiter or hiring manager, ask about work-life balance, leadership principles, and other factors that affect productivity. Compare their responses to what employees have shared about their experiences.</p>
</li>
</ol>
<h2 id="heading-how-to-be-good-at-real-world-hld"><strong>How to be good at real world HLD?</strong></h2>
<p>This response slightly overlaps with a <a target="_blank" href="https://blog.gagan93.me/11-conversations-with-10-software-engineers#heading-how-to-know-about-scale-without-working-on-scale">previous section</a>. You don’t easily get the chance to build something large or join a team scaling their systems. The best thing you can do without much investment is to learn from others' designs and mistakes.</p>
<ol>
<li><p>IIf you work for a company with large systems, talk to the people who built them or have been working on them for a long time. Read documentation to understand design decisions and how they've evolved. Look for <a target="_blank" href="https://github.com/joelparkerhenderson/architecture-decision-record">ADR</a> documents in large companies to learn the reasons behind these decisions.</p>
</li>
<li><p>If you're on a small team with scaling products, it's a great opportunity to learn how to optimize the system for scaling without overspending. Understand how indexes speed up queries and which types you need. Learn about caching and cache invalidation (the <a target="_blank" href="https://martinfowler.com/bliki/TwoHardThings.html">second hardest thing in computer science</a>). Learn how you can write modular code to make future changes easier.</p>
</li>
<li><p>If you're on a team with unstable products, find out what causes outages and read past outage postmortems. You'll learn more from outage calls than from regular work. Don't miss this learning opportunity (I've also shared my insights from <a target="_blank" href="https://blog.gagan93.me/debugging-production-downtimes">debugging production outages</a> here).</p>
</li>
<li><p>If none of these apply to you, learn from others through newsletters, blogs, and videos that explain these concepts.</p>
</li>
</ol>
<p>Being good at HLD is not building scalable systems on day 1, but starting with a stupid solution and innovating on the go. And in this process, you should know (or learn) what part of systems can potentially be a bottleneck so that you plan your changes accordingly.</p>
<h2 id="heading-im-not-working-on-kubernetes-or-microservices-will-i-be-easily-able-to-switch"><strong>I’m not working on Kubernetes or Microservices, will I be easily able to switch?</strong></h2>
<p>No system needs micro-services architecture, an EKS cluster or Kafka to begin with. Even when they acquire real customers, they still might not need these technologies. Mature technical leaders know the challenge of introducing fancy parts in the infrastructure, and they make sure that you do not have them unless you can pay for the operational cost of running and maintaining them. It’s easy to spin a Redis cluster, an EKS cluster or even an API gateway with a few clicks on your favourite cloud provider. But it’s not easy to get away with these technologies once you’re in. For example, an EKS cluster has a starting cost of ~ $70-$80. In a similar cost, you can launch 2 small EC2 machines behind an Application loan balancer along with a small RDS. While EKS solves for your “scaling needs”, you don’t need to solve them on day 1. I remember my last company was running a single EC2 server that was also running background jobs in a 4 core, 16G machine when I joined in 2018. It worked pretty well for them at that time and we didn’t introduce an ALB till late 2018 because that has a fixed monthly cost + data transfer cost that no one wants to pay from the first day.</p>
<p>This question came from someone who had 5+ YOE experience but no experience working on microservices. My suggestion here is that you should read about microservices and when it’s good to have them in your system. I’ve worked on transitioning monolith to microservices after reading <a target="_blank" href="https://www.amazon.in/Monolith-Microservices-Sam-Newman/dp/1492047848">this book</a>, and trust me that it’s not everyone’s cup of tea. You need a dedicated team to work on it rather than a few engineers working on it part-time. Even then, there are high chances that you end up building a more convoluted architecture (some thoughts shared <a target="_blank" href="https://blog.gagan93.me/monolith-microservices">here</a>). For you as an engineer, it’s important to know all the challenges so that whenever you get a chance to do such a migration, your chances of being successful are high.</p>
<p>Working on distributed systems isn't something everyone gets to do, but it's important to understand their pros and cons. This way, future interviewers can assess your knowledge, and you won't appear uninformed about these systems. So be it microservices, kubernetes or containers — know little bit about everything even when you don’t get to work on them in production.</p>
<hr />
<h2 id="heading-conclusion">Conclusion</h2>
<p>Thank you for taking the time to read through this post. I hope the insights were worth the time you spent. Hearing firsthand about the diverse challenges, growth experiences, and problem-solving approaches was a reminder of how valuable open dialogue is for learning and professional development. Whether you’re navigating similar paths or different ones, I hope these reflections offer useful perspectives. Do share this with your friends who are looking for similar advice. Let me know your thoughts in the comments. See you in the next blog post 🚀.</p>
]]></content:encoded></item><item><title><![CDATA[Problems with generic code]]></title><description><![CDATA[Background
I’m a Senior Engineer with nearly 9 years of experience in the software industry. In one of my previous projects, I had to migrate the background job processing engine of a Ruby-based application from delayed-job to sidekiq. For those not ...]]></description><link>https://blog.gagan93.me/problems-with-generic-code</link><guid isPermaLink="true">https://blog.gagan93.me/problems-with-generic-code</guid><category><![CDATA[generic-code]]></category><category><![CDATA[refactoring]]></category><category><![CDATA[SOLID principles]]></category><dc:creator><![CDATA[Gagandeep Singh]]></dc:creator><pubDate>Sun, 22 Sep 2024 11:38:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/4bhhwmsYl-c/upload/8181429a14d1764f9498810f588ec991.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-background">Background</h2>
<p>I’m a Senior Engineer with nearly 9 years of experience in the software industry. In one of my previous projects, I had to migrate the background job processing engine of a Ruby-based application from <a target="_blank" href="https://github.com/collectiveidea/delayed_job">delayed-job</a> to <a target="_blank" href="https://github.com/sidekiq/sidekiq/">sidekiq</a>. For those not familiar with Ruby, think of these as two libraries that help you run async jobs in a distributed system, similar to <a target="_blank" href="https://www.jobrunr.io/en/">JobRunr</a> in Java projects or <a target="_blank" href="https://docs.celeryq.dev/en/stable/">Celery</a> in Python projects. We encountered several issues with the old library and realized that Sidekiq would be a much better option as we scaled. To summarize:</p>
<ol>
<li><p>Sidekiq runs multiple threads per process to execute jobs, while Delayed Job runs one thread per process. This means Delayed Job uses more memory for executing the same set of jobs per unit time, especially if your workloads are IO-bound.</p>
</li>
<li><p>Delayed Job uses SQL table to store jobs, whereas Sidekiq uses Redis. Redis, being an in-memory database, is much faster than traditional disk-based SQL databases.</p>
</li>
<li><p>Sidekiq is a very popular Ruby library, so there are many extensions available for performing other common tasks. These include <a target="_blank" href="https://github.com/sidekiq-cron/sidekiq-cron">running crons</a> and ensuring <a target="_blank" href="https://github.com/mhenrixon/sidekiq-unique-jobs">unique jobs</a>. Additionally, Sidekiq’s author has <a target="_blank" href="https://github.com/sidekiq/sidekiq/wiki/Using-Dragonfly">started supporting</a> a drop-in <a target="_blank" href="https://www.mikeperham.com/2024/02/01/supporting-dragonfly/">replacement for Redis</a> for storing jobs.</p>
</li>
<li><p>While many open-source extensions exist for Sidekiq, companies can get more features and premium support with <a target="_blank" href="https://sidekiq.org/products/pro.html">Sidekiq Pro</a>.Migration phase</p>
</li>
</ol>
<p>We added both libraries together for a few months and gradually migrated from DJ to Sidekiq. The two most common uses of background jobs were:</p>
<ol>
<li><p>Calling an instance method asynchronously, like loading a model entry from the database and calling a method on it asynchronously (e.g., <code>Model.find(some_id).process_it_async</code>).</p>
</li>
<li><p>Calling a class method asynchronously, which might initialize more classes or call methods as needed (e.g., <code>SomeClass.process_class_method_async(some_data</code>).)</p>
</li>
</ol>
<p>At that time, I was a typical <strong>startup engineer</strong> with limited knowledge of code quality, focused primarily on <strong>getting things done.</strong> Consequently, I wrote a small class that facilitated the migration of many usages to Sidekiq:</p>
<pre><code class="lang-ruby"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GenericAsyncWorker</span></span>
  <span class="hljs-keyword">include</span> Sidekiq::Worker

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">perform</span><span class="hljs-params">(klass, method, args)</span></span>
    <span class="hljs-keyword">if</span> args[<span class="hljs-symbol">:id</span>]
      klass.constantize.find(args[<span class="hljs-symbol">:id</span>]).send(method)
    <span class="hljs-keyword">else</span>
      klass.constantize.send(method, args)
    <span class="hljs-keyword">end</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>
</code></pre>
<p>This is not the exact code but gives an idea of what I wrote. In short:</p>
<ol>
<li><p>If <code>args</code> had an <code>id</code>, I loaded the model object and called <code>method</code> on it.</p>
</li>
<li><p>If not, I called the <code>class method</code> with the given <code>args</code>.</p>
</li>
</ol>
<p>For non-Ruby developers:</p>
<ol>
<li><p><code>include Sidekiq::Worker</code> makes this class a background job processor.</p>
</li>
<li><p><code>find</code> loads a table row from the database based on ID. It’s part of Rails’ ORM.</p>
</li>
<li><p><code>send</code> dynamically calls methods of a class.</p>
</li>
</ol>
<p>There was a very kind Senior Engineer who reviewed my code later and said, "<em>this is not good, this is too generic</em>." I didn't understand what they meant at the time. My thought process was — <em>it's working and it helped complete the migration faster, so what's the problem?</em></p>
<p>Before you read on, take a moment to think about whether this is good or bad.</p>
<h2 id="heading-the-problems">The Problems</h2>
<p>Even though the code above works, it breaks key principles of good, maintainable code:</p>
<ol>
<li><p>According to the <strong>S</strong> in <strong>SOLID</strong> principles, a class should have one reason to change. I also apply this to methods — a method should do one thing really well. This method is trying to do two things. You might think the method is small and only doing two things, so what's the issue? Read on to understand.</p>
</li>
<li><p>The code inside the <code>if</code> statement expects to load the model object and then call <code>method</code> on it. What if someone wants to pass arguments to that <code>method</code> also? Would we add another argument or <code>if</code> statement to handle that?</p>
</li>
<li><p>What if this class is a plain Ruby class (not a Rails model) that needs to initialize the constructor and then call the <code>method</code>? Another flag to handle that?</p>
</li>
<li><p>As you might know, we assign <strong>queues</strong> to process <strong>specific jobs</strong>. Queues are chosen based on the urgency of the task. If many tasks start running with this generic code, how do we determine the urgency of this job class?</p>
</li>
</ol>
<p>By just looking at the code for a minute, I quickly spotted these problems. I laugh now, thinking I wrote this back in 2019. But that's a good sign because if you don't like your old code, it means your design skills are matured 😇.</p>
<p>The above is an example of <strong>Generic Code.</strong> Such classes evolve into <a target="_blank" href="https://refactoring.guru/smells/long-method">long methods</a> with a <a target="_blank" href="https://refactoring.guru/smells/long-parameter-list">lot of arguments</a>, creating a mess. Over time, multiple developers change the code in these classes to fit new use-cases, often breaking existing ones. There are only two ways to solve such problems:</p>
<ol>
<li><p>Don’t create such classes in the first place.</p>
</li>
<li><p>If you created them somehow, gradually move to specific classes and delete the old one later.</p>
</li>
</ol>
<p>I don't suggest spending time untangling the mess built up over the years by different developers unless you have test cases and very strong reasons to do so. It's better to create specific classes for each use case and migrate to them slowly.</p>
<h3 id="heading-lets-talk-about-testing">Let’s talk about testing</h3>
<p><strong>Note</strong>: Many developers and teams don't write unit tests. If you're one of them, I strongly recommend you start. They make future maintenance easier and provide feedback on your design.</p>
<p>If you write tests regularly, you'll see that this class is hard to test. The reason is simple - it has a lot of generic behavior, making it difficult to identify all the combinations you need to test. From my past experience, <strong>if a class is hard to test, it likely needs refactoring</strong>.</p>
<p>I still see developers writing generic code for production projects. This isn't necessarily a problem, as they are on the same learning journey I once experienced. When I encounter this, I take the time to discuss it with them. I emphasize the importance of writing specific, maintainable code. I share my experiences and lessons learned over the years.</p>
<h3 id="heading-extending-the-same-thought-process-to-tables">Extending the same thought process to “tables”</h3>
<p>Developers love writing code that goes beyond their current needs. As we saw earlier, they often create solutions that are more complex than necessary. But this issue isn't just limited to classes — we also tend to generalize tables and columns to handle more problems than they were meant to solve.</p>
<p>Let's imagine a developer named "John" created a table called <code>users</code> to handle authentication for their app. At first, this table had only six columns:</p>
<pre><code class="lang-bash">                                 Table <span class="hljs-string">"public.users"</span>

Column         |            Type             | Nullable  |         Default
---------------+-----------------------------+-----------+----------+----------------------- 
 id            | <span class="hljs-built_in">integer</span>                     | not null  | nextval(<span class="hljs-string">'users_id_seq'</span>::regclass)
 first_name    | character varying           |           |
 last_name     | character varying           |           |
 email         | character varying           | not null  |
 created_at    | timestamp without time zone | not null  |
 updated_at    | timestamp without time zone | not null  |
</code></pre>
<p>As time went by, they realized they needed more columns like <code>middle_name</code>, <code>country_code</code>, <code>language</code>, and <code>timezone</code>. So, they added those columns as needed (sounds good, right?). As their product expanded to multiple countries, they needed to track specific attributes for each country. They decided to add a <code>jsonb</code> column to the <code>users</code> table to handle these “country-specific” attributes because some keys were valid in one country but not in another. Adding separate columns for each attribute seemed like overkill. Thinking ahead, they named this field <code>extra_attributes</code>.</p>
<p>A month later, another developer, Lee, decided to add their data to the <code>extra_attributes</code> column since it was a similar use case (but not dependent on country). Within a year, this column had different values for different users, and no one knew all the use cases for this column. This is why I sometimes don’t like NoSQL schemas — they are too flexible unless you have proper safeguards. To avoid this:</p>
<ol>
<li><p>John could have named this column something other than <code>extra_attributes</code> to indicate it was for storing country-specific attributes only (better naming).</p>
</li>
<li><p>John could have created different tables with a 1:1 relation to users for such use cases. Anyone could check the <code>country_code</code> column and figure out which table(s) are relevant for a specific country’s user (better design).</p>
</li>
</ol>
<p>Whenever we introduce a bit of <strong>generic behavior</strong> in our code or database design, it is likely to be misused later. This happens because everyone thinks differently and wants to deliver quickly 🚀.</p>
<p>This is just the first level of misuse. Another common <strong>generic mistake</strong> is creating tables like <strong>tags</strong> or <strong>attachments</strong>. Once these tables exist, everyone adds their own use cases, making them very large. I've seen cases where more than 50% of data is in a generic table, just to avoid adding a column to the primary table. While querying the primary table is easy, querying this table requires a large index, which can be even bigger than the table itself. The problems with large generic table are:</p>
<ol>
<li><p>You can’t archive old data even if the index and table size keep growing. So you end up paying the cost of keeping all this data in hot storage (eg. SSDs).</p>
</li>
<li><p>You can’t partition the table easily.</p>
</li>
<li><p>Queries become slower over time.</p>
</li>
<li><p>Internal tasks like <a target="_blank" href="https://www.postgresql.org/docs/current/sql-vacuum.html">Vacuuming</a> take longer.</p>
</li>
<li><p>When you upgrade your database, these tables take more time to analyze and start serving data.</p>
</li>
</ol>
<p>And all these problems exist because there are too many use cases being served, and you have no idea how your solution could affect the users. Just like with generic classes, the solution is to eliminate this table and gradually migrate the data to specific columns (or perhaps specific tables).</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>While generic code and database design can offer flexibility and quick solutions in the short term, they often lead to significant long-term challenges. Over-reliance on generic patterns makes testing difficult, as it’s hard to test all the supported cases. Similarly, abusing generic database structures can result in performance bottlenecks, scalability issues, and maintainability problems. The key takeaway is to strike a balance between flexibility and specificity. While some level of generalization is beneficial for code reuse and rapid development, it is essential to maintain a clear structure, define boundaries, and optimize for performance and future growth to avoid technical debt.</p>
<p>Thank you for reading! Have a good day.</p>
<hr />
<p>If you liked this post, please read these too:</p>
<ol>
<li><p><a target="_blank" href="https://blog.gagan93.me/multi-tasking-is-not-so-cool">Multitasking is not that cool</a>.</p>
</li>
<li><p>Are you planning to transition from monolith to microservices? Read <a target="_blank" href="https://blog.gagan93.me/monolith-microservices">this</a> once.</p>
</li>
<li><p>If you’re planning data migration from one system to another, read <a target="_blank" href="https://blog.gagan93.me/migrating-data-across-services">this</a>.</p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Blameless culture]]></title><description><![CDATA[Background
Mistakes are inevitable in any project, especially when it comes to complex software projects. However, the way we respond to those mistakes can make or break the motivation and productivity of our teams. This post discusses how fostering ...]]></description><link>https://blog.gagan93.me/blameless-culture</link><guid isPermaLink="true">https://blog.gagan93.me/blameless-culture</guid><category><![CDATA[Culture]]></category><category><![CDATA[team]]></category><category><![CDATA[motivation]]></category><dc:creator><![CDATA[Gagandeep Singh]]></dc:creator><pubDate>Sun, 22 Sep 2024 09:54:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/376KN_ISplE/upload/6e4bd656630fd860020b1b36dfc61098.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-background">Background</h2>
<p>Mistakes are inevitable in any project, especially when it comes to complex software projects. However, the way we respond to those mistakes can make or break the motivation and productivity of our teams. This post discusses how fostering a blameless culture can enhance team productivity.</p>
<h2 id="heading-what-is-a-blameless-culture">What is a Blameless Culture?</h2>
<p>Have you ever heard of teams where individuals blame each other when a product release is delayed? Or perhaps, unfortunately, worked with such teams? It is quite disheartening to be part of a team where such inefficiencies outweigh productive work. A few reasons why this happens are:</p>
<ol>
<li><p>If managers do not invest sufficient time in designing a system that promotes transparency,</p>
</li>
<li><p>Or when most of the team members encourage the blame-game, and no one steps up to highlight that this approach harms team motivation,</p>
</li>
<li><p>Or where it is normal to delay releases or shift responsibilities, rather than maintaining clarity.</p>
</li>
</ol>
<p>The above is exactly opposite of how <strong>Blameless culture</strong> looks like. In a Blameless culture, we emphasize on <strong>learning</strong> and <strong>improving</strong> but <strong>without pointing fingers</strong>. So when a mistake happens, the focus is on analysing the <strong>root cause</strong> and <strong>fixing it</strong>. In such a culture, people are open to communicate and accept their mistakes rather than hiding them or shifting blames.</p>
<h2 id="heading-role-of-processes">Role of processes</h2>
<p>Let’s say you work in a startup with less than 10 people. There are high chances that you won’t have a process defined for anything. So if you are working in the leadership team and someone comes and asks for a leave, or wants to get their laptop repaired, or needs a comfortable chair — you will need to define a process for them. On the contrary, in a large organisation you will see a process and budget defined for almost everything. While processes are important for any organisation, startups can’t prioritise defining these processes, as their goal is to get the first few paying customers for their product(s). At the same time, they can’t even inherit the processes from a large company because a lot of those will not make sense for a startup.</p>
<p>As of now, I have over 9 years of industry experience, all in startups (including one at a <strong>very early stage</strong>). I believe that team members should be open to learning new processes to tackle new challenges. They shouldn't have a fixed mindset that makes them think in just one way. What worked at your previous company might not work at the next one. So, having an open mindset helps you adapt to the environment and come up with new, specific, and innovative solutions.</p>
<p>Processes in startups evolve as the company grows. For instance, they determine which leave plan is effective and what kind of release schedule works best for their software. The evolution of these processes also addresses many cultural issues. I recall in my previous role, we began to develop a culture of shifting blame to individuals and teams when our product manager inquired “<strong>Why is the Android release delayed?”</strong></p>
<ul>
<li><p>For the frontend team, it was the QA team that approved the API too late.</p>
</li>
<li><p>For the QA team, it was the backend team whose estimates were off by days or weeks.</p>
</li>
<li><p>For the backend team, it was the product team that didn't consider all scenarios, leading to changes in the code design after they started building the backend..</p>
</li>
</ul>
<p>We saw this as a chance to improve our processes. It took us 3-4 retrospective meetings to identify what needed improvement. Our <strong>open mindset</strong> helped us eliminate the <strong>wrong culture</strong> and address the <strong>root cause</strong> of our problem. I also wrote a detailed <a target="_blank" href="https://blog.gagan93.me/efficient-fullstack-delivery">blog post</a> about it many years later.</p>
<h2 id="heading-culture-ltgt-accountability">Culture &lt;&gt; Accountability</h2>
<p>Blameless culture doesn’t mean avoiding accountability; it means redefining it. Accountability is about ownership of tasks and outcomes. When an issue arises, the goal is to improve the process and systems that allowed the problem to occur, rather than penalizing the individual (or team) involved. This empowers team members to take responsibility without fear.</p>
<p>Another story I remember is from a recent interaction with a junior colleague in my last role. Both of us were in our notice period at that time. I met them face to face for the first time on their last working day 😅. They were moving to a large investment bank as a Software Engineer. Moving from a startup to a big company that deals with money, they were worried about what would happen if their code caused a loss of real money in production. My shortest answer to them was —</p>
<blockquote>
<p><em>If it’s easy to cause loss of money in their systems, it’s a process problem and not an engineer’s fault. There should be sufficient processes and checks in place to make sure this doesn’t happen very easily</em>.</p>
</blockquote>
<p>Similarly, every now and then, I see a meme where an intern claims to accidentally destroy the production database. The problem is not the intern, but the policies that gave them write access to production database 😄.</p>
<h2 id="heading-founding-teams-impact">Founding Team’s Impact</h2>
<p>The founding team has a profound impact on a company's culture and processes, Founders establish the core values that reflect their work ethic and vision, setting the tone for how employees interact, make decisions, and solve problems. Whether intentional or not, the founding team’s leadership style influences the company’s norms — whether it encourages open communication, innovation, or strict hierarchy. I recently interacted with an old friend who works as a Senior Business analyst. They recently changed their job and told that the culture is very poor. In their words:</p>
<blockquote>
<p>Our managers do not treat their reportees well. If you don't answer their Slack messages quickly, they'll ping others in the team asking, "Where is XYZ person and why aren't they responding?" It doesn't matter if the person went to the washroom for five minutes. They expect immediate responses to everything.</p>
</blockquote>
<p>When I asked about their skip manager’s behavior, they said, <em>"Everyone is the same here. Skip managers shout at managers, and managers shout at us."</em> 😿 This shows how a bad culture spreads from top to bottom. Another example is a story shared by <em>The Pragmatic Engineer</em> in <a target="_blank" href="https://newsletter.pragmaticengineer.com/p/stripe-part-2">their blog</a>. It’s about Stripe’s <strong>strong writing culture</strong>. Because their CEO and CTO write a lot, it encourages everyone to do the same. As a result, all engineers and managers write long internal documents. I wrote about the impact of the founding team on the rest of the team on <a target="_blank" href="https://www.linkedin.com/posts/gagan93_culture-writing-team-activity-7170322475212374016-UjEc/">my LinkedIn</a> a few months ago:</p>
<blockquote>
<p>If you're an old member of a team, your work is beyond your work profile. One major role is to ensure a positive team culture. Culture propagates in two ways:  </p>
<p>1. Vertically (CEO -&gt; Dept leads -&gt; managers -&gt; seniors -&gt; freshers).<br />2. Horizontally (Among peers / among old and new people).  </p>
<p>If there is a blame-game culture in a team of 10 members, then chances are that 11th member will also start doing that, or will leave. On the positive side, if there's a bar of "high quality" code or processes in a team, then the new member will have to match that in order to survive.</p>
</blockquote>
<h2 id="heading-conclusion">Conclusion</h2>
<p>If you work in a team with cultural issues, collaborate with your peers and manager to improve it. If you're in a startup or a growing team, make sure the culture develops positively. Small habits have a big impact when others follow them, whether good or bad.</p>
<p>Thank you for reading! Have a good day.</p>
<hr />
<p>If you liked this post, please read these too:</p>
<ol>
<li><p>If your team glamorizes overworking, please read <a target="_blank" href="https://blog.gagan93.me/eight-productive-hours">this</a> (make them read too!)</p>
</li>
<li><p>Inculcate a culture of <a target="_blank" href="https://blog.gagan93.me/asking-good-questions">asking good questions</a> in your team.</p>
</li>
<li><p>Read this small post about <a target="_blank" href="https://blog.gagan93.me/testing-an-app">testing and test suite</a>.</p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Unseen risks of Inheritance]]></title><description><![CDATA[Background
I started learning Object-Oriented Programming (OOP) with C++ during my college days, and Inheritance is one of the key concepts you can't miss when learning OOP. In fact, inheritance is one of the four core principles of OOP, along with E...]]></description><link>https://blog.gagan93.me/unseen-risks-of-inheritance</link><guid isPermaLink="true">https://blog.gagan93.me/unseen-risks-of-inheritance</guid><category><![CDATA[inheritance]]></category><category><![CDATA[OOPS]]></category><category><![CDATA[Design]]></category><dc:creator><![CDATA[Gagandeep Singh]]></dc:creator><pubDate>Sun, 25 Aug 2024 11:58:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/s56H08cAlM0/upload/1de6984ffea5c6f46cdae4bb876e1080.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-background">Background</h2>
<p>I started learning Object-Oriented Programming (OOP) with C++ during my college days, and <strong>Inheritance</strong> is one of the key concepts you can't miss when learning OOP. In fact, inheritance is one of the four core principles of OOP, along with <strong>Encapsulation</strong>, <strong>Abstraction</strong>, and <strong>Polymorphism</strong>. In our textbooks, the examples of inheritance were quite simple. The one I remember involved a <code>Shape</code> class, where classes like <code>Circle</code>, <code>Square</code>, and <code>Rectangle</code> inherited from <code>Shape</code> and override the <code>area</code> method. Unfortunately, real-life classes are never that simple 😄.</p>
<h2 id="heading-problems-with-inheritance">Problems with Inheritance</h2>
<p>I have written a significant amount of code using the Inheritance pattern, which complicated the overall design of my system over time. A common issue was that many classes in the hierarchy contained methods that were not useful to them. These methods were inherited from somewhere in the inheritance chain. This problem often arises when you use this pattern early in the design and fail to refactor the design when things become more complicated. So basically, if you stick with the design and keep adding more behavior to the classes without rethinking the design for new requirements, you'll end up with the same problem. As a result, your classes might have methods that don't make much sense for them. A few other problems that come with Inheritance are:</p>
<ol>
<li><p><strong>Tight coupling</strong> - Inheriting classes from each other inevitably increases coupling. While changes in a subclass do not affect the parent class, the reverse is not true. In a lengthy inheritance chain, any modifications to the base class design can impact all subclasses.</p>
</li>
<li><p><strong>Long chain -</strong> Inheritance is beneficial only when the chain is kept short. In a long chain of classes, it becomes challenging to trace the origin of method definitions and understand where they are overridden. Such a design complicates debugging and maintenance.</p>
</li>
<li><p><strong>Rigid design</strong> - Because of the coupling introduced by inheritance, the design becomes less flexible and more rigid. When you make modifications to the base class, these changes can unintentionally affect all derived classes. This can lead to unexpected bugs or require changes in multiple places throughout the codebase.</p>
</li>
</ol>
<h2 id="heading-why-people-still-use-it">Why people still use it?</h2>
<p>We all know that <strong>low coupling</strong>, <strong>ease of debugging</strong> and <strong>flexibility</strong> are the traits of a good design. So why should we use Inheritance if we do not get all of this? The answer to this is <strong>—</strong> Inheritance is beautiful, only if you know how to do it nicely. For example, the Java's collection framework hierarchy neatly implements inheritance:</p>
<p><img src="https://upload.wikimedia.org/wikipedia/commons/a/ab/Java.util.Collection_hierarchy.svg" alt="Java collections framework - Wikipedia" /></p>
<p>The above design is better because:</p>
<ol>
<li><p><strong>Clean Abstraction</strong> - It uses abstract classes and interfaces. Your language might not provide the same functionality (e.g., <strong>Ruby</strong> doesn't have <strong>interfaces</strong>), but the general idea of breaking down the functionality should help.</p>
</li>
<li><p><strong>Refactoring</strong> - They clearly define the role of each level, and they have been heavily refactoring the hierarchy since the collections framework was released in 1998.</p>
</li>
<li><p><strong>Design Patterns</strong> - Beyond just inheritance, the framework also uses some design patterns internally to simplify the design.</p>
</li>
<li><p><strong>Inheritance Chain</strong> - Although there is no strict rule on how long or wide your inheritance hierarchy should be, it's good to keep an eye on it so you don't end up with 10-20 levels of inheritance.</p>
</li>
</ol>
<p>Beyond these points, the classes in the Java collection framework adhere to the SOLID principles, which are crucial for maintaining a robust and flexible design.</p>
<h2 id="heading-clean-inheritance">Clean Inheritance</h2>
<p>There's an excellent talk by <a target="_blank" href="https://sandimetz.com/">Sandi Metz</a> from RailsConf 2014 (<a target="_blank" href="https://www.youtube.com/watch?app=desktop&amp;v=8bZh5LMaSmEhttps://www.youtube.com/watch?app=desktop&amp;v=8bZh5LMaSmE">link</a>) where they explain how they refactored a code with high cyclomatic complexity to a much simpler design using Inheritance. While they are on it, they also explain when inheritance is good. Quoting from the same talk:</p>
<p><em>Despite of what you have have heard, Inheritance is not evil, and I can tell you exactly when it's safe to use it. Here's what you want - You want a shallow, narrow hierarchy. You don't want it to be deep, and you don't want it to be wide, alright? If that's the problem you have, there's no better solution that inheritance and you're free to use it. Inheritance is not evil, but sometimes we are.</em></p>
<p>Although I've provided the gist of the talk in context of inheritance, there are a lot of other good things that you can learn from the talk, so you must watch it.</p>
<h2 id="heading-other-patterns">Other patterns</h2>
<p>I also wrote a bit about the use of design patterns in the Java collections framework. That's something I'd like to explain further. As a developer, you should always look for better ways to write cleaner, simpler code. So beyond inheritance, try these things:</p>
<ol>
<li><p><strong>Small classes</strong> - The entire idea of maintainable software comes from writing small, extensible classes that can work together to complete a task. Make sure that your classes do a single task nicely. If you feel that the new requirements are leading to some bloat in the existing class, plan to refactor it rather than adding behaviour that doesn't belong to it. I'll touch on <em>refactoring safely</em> in my next blog.</p>
</li>
<li><p><strong>Design patterns</strong> - There's nothing new that I'm suggesting here. It's important for all engineers to read about design patterns at least once so that you do not make the same mistakes that these patterns solve. I've personally found <a target="_blank" href="https://refactoring.guru/design-patterns">this website</a> excellent for design patterns (and <a target="_blank" href="https://refactoring.guru/refactoring/smells">code smells</a>) but it's ok if you've read <a target="_blank" href="https://www.amazon.in/Design-Patterns-Object-Oriented-Addison-Wesley-Professional-ebook/dp/B000SEIBB8">GOF</a> or some other book. I've personally used Facade, Adapter, Decorator, Proxy, Strategy and a few more design patterns in my projects to simplify the design.</p>
</li>
<li><p><strong>Composition over Inheritance</strong> (<a target="_blank" href="https://www.geeksforgeeks.org/favoring-composition-over-inheritance-in-java-with-examples/">more here</a>) - A lot of code that we write using Inheritance can be simplified using Composition pattern. By composing objects from simpler, reusable components, you create more flexible and maintainable code. Composition allows you to change or extend behaviours dynamically without altering existing code, unlike inheritance that leads to rigid hierarchies and tight coupling.</p>
</li>
</ol>
<h2 id="heading-conclusion">Conclusion</h2>
<p>While inheritance has been a cornerstone of Object-Oriented Design, it's important to recognise its limitations and explore alternatives that offer more flexibility and maintainability. As your software evolves, adopting these approaches can lead to more maintainable code.</p>
<hr />
<p>If you liked this post, please read these too:</p>
<ol>
<li><p><a target="_blank" href="https://blog.gagan93.me/avoid-redundant-complexity"><strong>Avoiding design complexities</strong></a> when building something simple.</p>
</li>
<li><p>Read about <a target="_blank" href="https://blog.gagan93.me/cloud-pricing-vendor-lock-ins"><strong>vendor lock-ins</strong></a> in detail.</p>
</li>
<li><p>If you work in a fullstack team that struggles to deliver efficiently, then <a target="_blank" href="https://blog.gagan93.me/efficient-fullstack-delivery"><strong>this is for you</strong></a>.</p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Branch International Interview Experience]]></title><description><![CDATA[Background
After giving a lot of interviews in small and large companies, I've joined Branch International this month. As I've shared my past interview experiences here, I'm sharing this one too. My profile was shortlisted for the position of Senior ...]]></description><link>https://blog.gagan93.me/branch-international-interview-experience</link><guid isPermaLink="true">https://blog.gagan93.me/branch-international-interview-experience</guid><category><![CDATA[branch]]></category><category><![CDATA[interview]]></category><category><![CDATA[senior-software-engineer]]></category><dc:creator><![CDATA[Gagandeep Singh]]></dc:creator><pubDate>Thu, 27 Jun 2024 05:28:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1719206976470/c5e3bf7e-a379-44f9-b629-300addb76dee.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-background">Background</h2>
<p>After giving a lot of interviews in small and large companies, I've <a target="_blank" href="https://www.linkedin.com/feed/update/urn:li:activity:7203321685398212609/">joined Branch International</a> this month. As I've shared my past interview experiences <a target="_blank" href="https://blog.gagan93.me/tag/interview">here</a>, I'm sharing this one too. My profile was shortlisted for the position of <strong>Senior Software Engineer</strong> through the <a target="_blank" href="https://tophire.co/">Tophire</a> platform. After that, their account executive contacted me. Once they finished, I was connected to a recruiter from Branch, who explained the company and the role to me. Those who do not know much about the company:</p>
<ol>
<li><p>Branch is a <strong>registered non-banking financial company</strong> (NBFC) regulated by the RBI, offering services in India and select international markets</p>
</li>
<li><p>Branch's <strong>fully-remote team</strong> is spread across India, the US, and Africa, bringing together diverse expertise.</p>
</li>
<li><p>We offer a range of financial solutions, including <strong>micro-loans</strong> and <strong>investment products</strong>, to help you achieve your financial goals</p>
</li>
</ol>
<h2 id="heading-interview-process">Interview Process</h2>
<p>After speaking with the Tophire account manager and the company's recruiter, these were the interview rounds:</p>
<ol>
<li><p><strong>Hiring Manager Conversation</strong> - This round was scheduled with the Chief Technology Officer. The aim of this round was to understand my current role, aspirations and what I am looking for in the next role. We discussed about my projects and about a few more technical things briefly. I took this as a chance to learn more about the company and their tech stack.</p>
</li>
<li><p><strong>Technical Screening Round</strong> - This round was scheduled with a Senior Engineer from the India team. It consisted of two parts - a data structures problem (easy) and rapid questions and answers based on web. I solved the data structures problem on <a target="_blank" href="https://coderpad.io/">Coderpad platform</a> where I could choose any language to implement the solution. I was able to wind up with a working solution in 18-20 mins. There were no modifications done in the problem statement to make it harder so we moved to the next part. The rapid fire QnA part was quite unique. The questions were related to databases, security, scalability, low-level design, authentication mechanisms, etc. No coding was required to answer these questions, but they were great for assessing someone's experience in backend development.</p>
</li>
<li><p><strong>Low Level Design Round</strong> - This round was scheduled with a Senior Engineer from the US team. The problem statement was designed in a way to see how would someone design APIs. I did not write any code in this round. We picked one problem and discussed how the APIs would look for that problem. Apart from a few APIs, rest of them were quite simple. In these (slightly complex) APIs, there were multiple ways to implement but I chose the RESTful method and explained the reasoning behind my decision. For a very specific requirement, I was also asked to write a SQL query.</p>
</li>
<li><p><strong>High Level Design Round</strong> - This round was scheduled with an Engineering Manager from the India team. The problem statement was around one of their products. I started sketching the system after understanding the initial requirements. They asked about the role of each box (the service) in my design, the choice of database, protocols etc. After discussing each design decision in detail, we also discussed how I would test, deploy and monitor this system. We winded up the discussion in an hour.</p>
</li>
<li><p><strong>Presentation Round</strong> - This round was very unique where I was asked to prepare a presentation on my past projects and present that in front of a panel. The panel included all the previous round interviewers, and a few more people. I had submitted the presentation to company recruiter few days before the round. I presented 3-4 major projects and the panel cross questioned me wherever they needed more details. According to their job description, this round was to better understand:</p>
<ol>
<li><p>Verbal and written communication skills, and ability to handle questions,</p>
</li>
<li><p>Complexity and scale of work,</p>
</li>
<li><p>Ability to execute on a project, in particular around evaluating tradeoffs and making decisions</p>
</li>
</ol>
</li>
<li><p><strong>Final Discussion Round</strong> - After clearing all the rounds, the company recruiter informed me that they want to extend an offer and scheduled a call with the hiring manager again. During this call, the hiring manager asked for any feedback I had about the overall process. They explained the compensation structure, and I asked a few questions that I had noted down. They gave me time to think before accepting the offer.</p>
</li>
</ol>
<h2 id="heading-conclusion">Conclusion</h2>
<p>After careful consideration, I accepted the offer and resigned from my position at LocoNav where I had worked for over six years. It was a tough decision to leave after spending such a long time. I joined Branch on June 3, 2024, and it has been great since then.</p>
<hr />
<p>If you enjoyed reading this, please check out my other blogs:</p>
<ol>
<li><p><a target="_blank" href="https://blog.gagan93.me/atlassian-interview-experience"><strong>Atlassian experience</strong></a>, <a target="_blank" href="https://blog.gagan93.me/stripe-interview-experience"><strong>Stripe experience</strong></a> and a short post around <a target="_blank" href="https://blog.gagan93.me/titles-inflation-and-down-levelling"><strong>job titles</strong></a>.</p>
</li>
<li><p>Today's SaaS products uses a lot of services from cloud providers that locks them into one provider. Read about <a target="_blank" href="https://blog.gagan93.me/cloud-pricing-vendor-lock-ins"><strong>vendor lock-ins in cloud</strong></a>.</p>
</li>
<li><p>More than building a large system, it's important to <a target="_blank" href="https://blog.gagan93.me/avoid-redundant-complexity"><strong>build it simple</strong></a>.</p>
</li>
<li><p>Propagate a culture of asking questions in your team. Learn to <a target="_blank" href="https://blog.gagan93.me/asking-good-questions"><strong>ask good questions</strong></a>.</p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Tech Interview Mistakes]]></title><description><![CDATA[Background
There are a lot of mistakes people make while preparing for and giving technical interviews. Like everyone else, I also made a lot of mistakes during my interview journey and learned the hard way (by getting rejected). Generally speaking, ...]]></description><link>https://blog.gagan93.me/tech-interview-mistakes</link><guid isPermaLink="true">https://blog.gagan93.me/tech-interview-mistakes</guid><category><![CDATA[interview]]></category><category><![CDATA[software design]]></category><category><![CDATA[mistakes]]></category><dc:creator><![CDATA[Gagandeep Singh]]></dc:creator><pubDate>Fri, 24 May 2024 08:41:21 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/hHg9MC-G8_Y/upload/6f324ad2452b8b16255fc6ed35fa94f0.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-background">Background</h2>
<p>There are a lot of mistakes people make while preparing for and giving technical interviews. Like everyone else, I also made a lot of mistakes during my interview journey and learned the hard way (by getting rejected). Generally speaking, rejections shouldn't hurt you because they teach you a lot. But in tech interviews, there are scenarios when things are outside your control, despite the best preparation. We'll discuss about such scenarios in the end of this post.</p>
<p>To start with, I'll divide this post into two parts:</p>
<ol>
<li><p>Preparation mistakes (during preparation).</p>
</li>
<li><p>Execution mistakes (during interview).</p>
</li>
</ol>
<h3 id="heading-during-preparation">During Preparation</h3>
<p>When I started looking out, I was completely unaware of industry expectations. I got in touch with a few people who had switched roles in last few years to understand the same. Apart from industry expectations, there were many other things that I had to know, like:</p>
<ol>
<li><p>How titles work in different companies?</p>
</li>
<li><p>How to prepare for the specific rounds? What sources are good (free / paid)?</p>
</li>
<li><p>How interviews are different from real work?</p>
</li>
<li><p>Will the companies hire me if my tech stack doesn't overlap much with theirs?</p>
</li>
</ol>
<p>The more you know about all these things, the better you can prepare and manage your expectations. Let's talk about all these things one by one:</p>
<ol>
<li><p><strong>Not understanding industry expectations</strong> - There are two types of companies (or roles that are open). Some roles explicitly mention that you need to be an expert in a specific technology stack to even get shortlisted. The other category is of roles that require you to be a good problem solver who can learn the tech stack and get things done. Both the roles have their own significance and we cannot blame the hiring team for being stringent in the first case. If the product is in early stage, the company would prefer to hire someone who is familiar with the stack so that they can contribute fast without the need of ramping up. If the product is in later stage, it's upto the choice of hiring team - they could hire people from a different technology stack, or they may still prefer to hire people with the same stack only. It depends on the stack also - incase they're using something very niche (for which developers are not available easily), the hiring team relaxes the tech stack expectations. But incase hiring for that tech stack is easy, they'd prefer someone who knows the stack. In my experience, it's easier for companies to find Java developers so rather than hiring for <em>Senior Software Engineer</em>, they put the job title as <em>Senior Java Engineer</em>. For medium-large organisations, the positions are tech stack agnostic for candidates who are good at problem solving. In the first case, some interview rounds focus on specific technologies (e.g., Core Java, Spring Boot). In the second case, you can choose your favorite programming language for the coding rounds.<br /> The above information is from my job hunt experience in the last 6-8 months. Obviously, exceptions are always there.</p>
</li>
<li><p><strong>Strong tech stack inclination</strong> - This point is interesting because, deep down, programmers have strong opinions about languages. For example, some people hate PHP (<a target="_blank" href="https://www.reddit.com/r/PHP/comments/1fy71s/why_do_so_many_developers_hate_php/">example</a>) while some make fun of java for it's verbosity (<a target="_blank" href="https://www.youtube.com/watch?v=kdMG40wUCm4">example 1</a>, <a target="_blank" href="https://www.youtube.com/watch?v=m4-HM_sCvtQ">example 2</a>, <a target="_blank" href="https://github.com/Hello-World-EE/Java-Hello-World-Enterprise-Edition/tree/master/src/com/example">example 3</a>). This could still be seen as a personal choice. The bigger issue comes when people <strong>associate themselves</strong> with one tech stack only. Let's take my example - I've been majorly writing Ruby for the past 9 years but I've also written a little bit of C, Java for Android, Powershell, Bash, jQuery, BackboneJS, and a few other languages in my current and previous role. While I too have opinions about what languages to avoid, I did not limit my job search to Ruby/Rails openings only. Limiting my job search to only Ruby/Rails positions would have been stupid, especially in the current market where openings are less. I'd recommend the same to everyone - do not call yourself a Ruby developer, Java developer or GoLang developer. Call yourself a Software Engineer who can learn syntax and be productive in any language. Having this mindset helps a lot in the long run.</p>
</li>
<li><p><strong>Confusion in job titles</strong> - This is the most important thing to know when you're searching for a new role. Some websites allow you to search jobs with years of experience, while some websites (like LinkedIn) allow you to search with job titles. LinkedIn also helps you to set job alerts for specific titles. Based on multiple factors, you could be eligible for multiple roles. Taking my example again - I work as a Staff Software Engineer in a small team but when it comes to job search, I search for Software Engineer, Senior Software Engineer, Staff Engineer, Team lead and Engineering manager roles. All these roles overlap with my experience range based on the size of organisation where I'm applying. I've already explained this point in a <a target="_blank" href="https://blog.gagan93.me/titles-inflation-and-down-levelling">detailed blog post</a>, so I'll rather leave you with a link to read the same.</p>
</li>
<li><p><strong>Building Resume only once</strong> - Resume building is an underrated exercise. When you're looking out, there are high chances that you're applying to many positions without a referral. In such cases, your resume is your first impression. If you've not spent enough time in build the same, there are high chances that you'll not get calls from good companies. Resume building is not a one time effort. You need to work on it continuously to reword your tasks in a more impactful manner. Just like job titles, I've already written a <a target="_blank" href="https://blog.gagan93.me/improving-your-resume">detailed blog post</a> on how I did more than 5 iterations on my resume in past 8-9 months. Even today, I don't think my resume is the best it can be, so I keep improving it.</p>
</li>
<li><p><strong>Not preparing an Introduction</strong> - In all the companies, the first thing people ask is to introduce yourself. In some companies, each interviewer asks this question (although that sounds repetitive). Generally, you get a minute or less to talk about yourself before they ask about your specific projects or give you a problem to solve. You might have built a lot of products but it's important to summarise your journey in this one minute and talk about everything that you find important. Write a long introduction of yourself, use AI to summarise it and keep improving it (just like your resume). I skipped this initially and found myself struggling while explaining my role/journey. I clearly remember that I forgot to explain to one employer that I manage a team of seven people in my current role. So my role might have sounded more of an <strong>IC</strong> than a <strong>Manager</strong> that could have good/bad impact based on what they were hiring for.</p>
</li>
<li><p><strong>Applying directly to dream companies</strong> - If you're interviewing after a long time, do not get discouraged by rejections. Despite building a lot of things and being hands-on after 9 years of total experience (6+ in my current role), I faced a lot of rejections in the beginning. The current way of tech hiring is not the best way to hire good candidates, it's a way to filter out candidates who are not interview ready. And being interview ready means something else for technical roles. You might not have built anything in real world but if you are prepared for the interviews, you'll get the job. And the reverse is also true. For this reason, it is important to not apply for your dream companies directly (in case you are targeting some). Do many interviews at different companies to see if you can handle the questions under pressure before applying to your target company. Given the competitive nature of the market, I've heard from many experienced people that you should give a few interviews every year even if you're not looking out. It's just to bridge the gap between your current preparation and market expectations.Another way of practising to a decent level is by taking mock interviews. While there are many paid websites for the same, a free solution for the same is by using ChatGPT as your interviewer. I've tried it and it is decently good, if not the best. You need to do some initial conversation with it to explain what it is supposed to do (otherwise it will give you both question and answer). I'll share my prompt for reference:</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1716527403115/b45bfbe5-3746-4b7d-9619-9ac54d7f6020.png" alt class="image--center mx-auto" /></p>
<p> The above screenshot is of my conversation with ChatGPT when I was preparing for my final round at Bain &amp; Co (read the <a target="_blank" href="https://blog.gagan93.me/bain-and-company-interview-experience">complete interview experience</a>). It's important to set context for ChatGPT before you start the conversation. You can modify the above prompt for technical roles as well. I also prepared for System Design interview using this format.</p>
</li>
<li><p><strong>Overconfidence</strong> - Assuming that you already know everything can be bad. It happened with me when I was learning LLD and HLD. Because the problems seemed familiar/simple, I thought I'll be able to crack the rounds (till I failed 1-2 of them initially). Everything demands your time and some practice. Tech interviews demand even more practice because you're expected to build (LLD) or talk about a MVP (HLD) within a period of 40-50 minutes. Although problems seem simple, thinking about the models, APIs, writing code, or talking about high level design, databases, protocols, challenges within this time period is very hard if you've not spent time in practising the same. I always recommend everyone to solve at least 5-10 problems for both LLD/HLD (without looking at the solutions) before appearing for any company.</p>
</li>
</ol>
<p>These are the top six things that came to my mind from my experience. Now let's talk about mistakes during interviews.</p>
<h3 id="heading-during-interviews">During Interviews</h3>
<ol>
<li><p><strong>Not talking enough / talking a lot</strong> - In an interview, you have an hour to talk about yourself and showcase your skills. It's important that you portray best of your skills. A good technical interview is a collaborative exercise where you and your interviewer solve a particular problem. In most of the cases, the interviewer has a fair idea around possible solution(s) because they have asked the same problem many times. A good candidate would ask questions (not cleverly seek hints) to understand the problem in depth before jumping towards the solution. There are generally three technical rounds - Data Structures, LLD and HLD. While Data structures question is a mathematical question (i.e. can be judged as right or wrong), the other two are somewhat open-ended. In many cases, ambiguities are present in the problem statement (even in DS round) to see how the candidate interprets the problem and asks questions. It's our responsibility to make sure that we're querying the interviewer to frame the problem better, scope the boundaries of the problem and present a solution that works for it. It's quite possible that some iterations are needed to tailor the solution to cover edge cases or changing requirements. I'll just recommend to keep on talking with your interviewer to make share what's going on your mind. On the opposite side, don't keep talking so much that you don't leave space for them to talk or share their feedback on your solution. A balance is important here. In my experience as an interviewer, very few candidates talk more than what is required, but talking less is a common problem.</p>
</li>
<li><p><strong>Assuming without discussing</strong> - While you're talking, make sure that you don't <strong>assume</strong> anything in your mind. Assumptions can lead to different solutions, hence it is important to speak about your assumptions. If that assumption is wrong, your interviewer will point out the same. For example: In most of the LLD problems if I'm talking about APIs, I assume that authentication is always in place and some header is coming in all the requests that is interpreted by the server for identifying the user. While most of the interviewers agree to this assumption, one of the interviewer asked this question- <em>What are the different authentication mechanisms you know about, and which one you'd like to use here (and why)?</em></p>
</li>
<li><p><strong>(Design Interviews) Stubborn with choices</strong> - In design interviews, you try to solve a problem by using some components like Databases, Load balancers, API Gateways, Firewalls, Protocols, API Servers, etc. Let's take an example of a System Design interview. The most common discussion that happens in almost all the interviews is around the choice of database - SQL vs NoSQL (let's assume we're comparing SQL databases with Document oriented databases like Mongo/Elastic). We need to have strong reasons to choose one kind of database for the specific use case rather than <a target="_blank" href="https://www.youtube.com/watch?v=b2F-DItXtZs">this guy</a> who is a MongoDB fan. There are many more places in a design interview where we need to make choices (eg. protocols). Just choosing one side is not enough - you need to justify your choice (or your interviewer will ask you to). Stubborn candidates leave a very bad impression on the interviewer and usually get rejected.</p>
</li>
<li><p><strong>(Design Interviews) Finding the core problem / not catching hints -</strong> In both LLD and HLD interviews, there is <strong>one core problem</strong> that we're solving. As we talk more with our interviewer and frame the actual problem from that <em>one liner ambiguous problem statement,</em> we get closer to the problem. For example:</p>
<ol>
<li><p>In a cab booking platform like Uber, matching the driver with rider could be a core problem because you need to choose the drivers using some algorithm (you can't just search them based on displacement as they might be a river apart).</p>
</li>
<li><p>In a movie booking platform like BookMyShow, holding the seats while user is in process of paying for the tickets, or releasing the seats when payment fails / times out could be the core problems.</p>
</li>
</ol>
</li>
</ol>
<p>    Similarly, your problem statement has one core problem which you need to identity. Although other parts of the systems are also important but this is one part that interests the interviewer. While you're talking with your interviewer, try to see if they're impressed with your design or are they like - <em>there's something more to the problem.</em> Incase you're missing something important or are going wrong, they try to give you a hint (eg. <em>How are you making sure that &lt;this&gt; problem is also addressed?</em> ). Do not miss these inputs, they may impact your overall design.</p>
<ol start="5">
<li><p><strong>Bad luck -</strong> In the beginning of this post, I wrote about a problem that is outside your control. That's bad luck. I'll share some scenarios to explain this:</p>
<ol>
<li><p><strong>Untrained interviewers -</strong> This usually happens in small companies where they're trying to expand the team. The interviewers might not be trained to conduct the discussion properly. Generally, some senior person <a target="_blank" href="https://www.metaview.ai/resources/blog/interviewer-shadowing-the-secret-of-the-most-effective-hiring-machines-in-the-world">shadows the new interviewer</a> to make sure they are ready to interview but in small companies (due to multiple things running in parallel) such processes are not mature and training is not sufficient.</p>
</li>
<li><p><strong>Stubborn interviewers</strong> - Not only candidates, interviewers can be stubborn. It's hard not only to work with them but even harder to do an interview. Such interviewers have one approach in mind and would expect you to give exactly same solution. I've faced this once where despite doing everything well, I was rejected. And this was because there was <strong>one thing</strong> that they wanted to happen in a certain way and I suggested something else (with a valid reason). This person also gave me feedback (I generally ask for the same at the end of interview) that I should have asked about scale of system (in a LLD problem) so that I had modified that one thing in my solution early. I've <strong>never seen</strong> anyone else talking about "scale of system" in a LLD interview. We generally go into details of API design, database design and the core problem.<br /> The best part - they were junior to me by designation, years of experience, and attitude towards better solutions :)</p>
</li>
</ol>
</li>
</ol>
<p>Apart from this, many other things (like mood of the interviewer) can impact your performance. Such things are generally not in your hands. Just like for candidates, interviewers also need training and feedback. In the organisations where interview process is more formalised and where interview calls are recorded, there are high chances that the interviewer would get feedback(s) if they need to improve on certain axes. In many organisations the feedback process is completely missing, which leads to bad candidate experience. If you face such a situation, you can also share your feedback with the recruiter / hiring manager. Make sure your tone is professional and not rude if you want them to consider your feedback.</p>
<hr />
<p>I hope the above points would help you to avoid mistakes that people (including myself) generally make. Good luck for your preparation 👍.</p>
<p>Incase you found this blog good, I'll suggest you to read these also:</p>
<ol>
<li><p><a target="_blank" href="https://blog.gagan93.me/tag/interview">Click here</a> to read all blog posts around <strong>interviews</strong>, including my interview experiences.</p>
</li>
<li><p>Adopt this thought process when <a target="_blank" href="https://blog.gagan93.me/fixing-code-exceptions">you're fixing exceptions</a>.</p>
</li>
<li><p>Make sure to avoid <a target="_blank" href="https://blog.gagan93.me/things-code-reviewers-hate">these mistakes</a> when you create pull requests.</p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Low Level Design Interviews]]></title><description><![CDATA[Background
I work as a Staff Software Engineer at LocoNav. When I give interviews with my current experience (that is close to 9 years), I am considered for these roles:

Software Engineer / Senior Software Engineer,

Staff Software Engineer / Lead,
...]]></description><link>https://blog.gagan93.me/low-level-design-interviews</link><guid isPermaLink="true">https://blog.gagan93.me/low-level-design-interviews</guid><category><![CDATA[interview]]></category><category><![CDATA[LLD]]></category><category><![CDATA[software design]]></category><dc:creator><![CDATA[Gagandeep Singh]]></dc:creator><pubDate>Fri, 17 May 2024 11:02:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/sc-B_2-Om7Q/upload/ab008fd3992eba621580c42b104d3255.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-background">Background</h2>
<p>I work as a Staff Software Engineer at LocoNav. When I give interviews with my current experience (that is close to 9 years), I am considered for these roles:</p>
<ol>
<li><p>Software Engineer / Senior Software Engineer,</p>
</li>
<li><p>Staff Software Engineer / Lead,</p>
</li>
<li><p>Engineering Manager (incase I want to move to a management role).</p>
</li>
</ol>
<p>The responsibilities for each role are highly dependent on the organisation's needs, and it doesn't harm me in the long run if I switch from <em>Staff Engineer</em> to <em>Senior Engineer</em> in my next role, as some might think (<a target="_blank" href="https://blog.gagan93.me/titles-inflation-and-down-levelling">explained here</a>). According to the role, the Low Level Design round and the overall interview process can be very different. Although the aim of this post is to talk about LLD with respect to interviews, but I believe it's a skill that should help you on your job as well. So don't limit your learning to interviews only.</p>
<h2 id="heading-what-is-lld">What is LLD?</h2>
<p>From wikipedia:</p>
<blockquote>
<p><strong>Low-level design</strong> (LLD) is a component-level design process that follows a step-by-step refinement process. This process can be used for designing data structures, required software architecture, source code and ultimately, performance algorithms.</p>
</blockquote>
<p>There's a reason for sharing this formal definition. Unfortunately, the <strong>interview directed preparation</strong> has spoiled the <strong>step-by-step</strong> nature of Low Level Design for many people. In many cases, the interviewers expect you to magically present the best design with working code and test cases within 45-50 minutes. While this is somewhat doable by practising the common problems, this is not what design means in Software Engineering.</p>
<p>I've given about 5-6 LLD interviews and all of them were not of the same kind. I'll first share <strong>how I prepared</strong> for the interviews and improved my low level design skills in general. After that I'll share <strong>my experience</strong> with different kind of LLD interviews I gave. In the end, I'll share different <strong>categories of problems</strong> that I found in different playlists, courses and interviews.</p>
<h2 id="heading-learning-and-improving-lld">Learning and Improving LLD</h2>
<p>Somewhere in late 2023, I was reached out by <a target="_blank" href="https://blog.gagan93.me/atlassian-interview-experience">Atlassian</a> recruiting team on Instahyre when they were hiring for the position of Senior Software Engineer. At that time, I was having zero preparation and almost zero idea of the overall process. Their team phone called me to explain the entire process and shared <a target="_blank" href="https://online.flippingbook.com/view/325478/">documents</a> that would help me to understand more about each round. I figured out there were three major things on the tech side - Data Structures, Low level design (LLD) and High level design (HLD/System Design). Going through details of each round, I figured out that I had practically worked on everything they would need, but I need to prepare in a way that I'm able to portray that during interviews.</p>
<p>As anyone would do, I searched for free content. There were plenty of excellent videos on YouTube but they varied in quality. This was a typical <a target="_blank" href="https://blog.gagan93.me/information-overflow">information overflow</a> problem - pick anything you want to learn and there will be 100 people on YouTube already sharing how to do it. Not all of them are the best, and almost none of them are wrong but you need to choose and stick to one or two (else you'll get lost).</p>
<p>This was the pattern of each channel/playlist that was teaching LLD:</p>
<ol>
<li><p>Teach SOLID principles,</p>
</li>
<li><p>Teach Design Patterns,</p>
</li>
<li><p>Choose 5-10 of the common problems (A TicTacToe Game, Snakes and Ladders Game, Elevators System, Building Splitwise, Car Rental platform, etc.). While solving these problems, they will try to fit in at least one / two design patterns in each problem.</p>
</li>
</ol>
<p>I think this is a pretty good roadmap if you're only targeting interviews. Practice 5-10 problems in a way that you do not look into solutions before attempting them on your own. If you look into their solutions, they look very obvious and you get fake confidence that you know everything. I was in this situation for both LLD/HLD rounds where I thought that I know everything just because I watched videos. It was only after getting rejected in good companies, I figured out that <em>things are not that obvious</em>.</p>
<p>I'll discuss little bit about each point before moving forward:</p>
<ol>
<li><p><strong>SOLID principles</strong> - These principles act as foundation of good design if you're implementing your solution in an Object Oriented Language. I'm personally a very big fan of <em>Single Responsibility Principle</em> than any other rule or design pattern. I think if someone understands this well, they can write pretty neat classes and ensure no unnecessary sharing of code/properties among classes. I'll not explain these principles in detail here, but I'll leave you with an <a target="_blank" href="https://www.freecodecamp.org/news/solid-principles-explained-in-plain-english/">excellent free blog</a> to read the same.</p>
</li>
<li><p><strong>Design patterns</strong> - These patterns are nothing but solutions to common problems that people face in Object Oriented Design. For example, in large projects, developers usually write layers of abstractions over language or frameworks. While designing these classes, they figure out that you need to provide a very simple and easy to use interface (API) for the end user. Such a pattern is known as a <a target="_blank" href="https://refactoring.guru/design-patterns/facade">Facade</a>. I've been using this pattern to build abstractions in my projects, even before I formally studied design patterns.<br /> If you're 3+ years of experience, there are chances that you've either read or heard about the <a target="_blank" href="https://www.geeksforgeeks.org/gang-of-four-gof-design-patterns/">GOF book</a>. This book is known to be the original work on design patterns. Although I got a chance to find <a target="_blank" href="https://refactoring.guru/design-patterns">this website</a> (they also have a PDF book) that has very good examples and illustrations incase someone wants to learn about Design Patterns. One bonus thing about this website is discussed in the next point.</p>
</li>
<li><p><strong>(Bonus) Code smells</strong> - Before I found a good content around code smells, I had read about a few of them in a separate blogs. Generally, no course suggests you to read about code smells, but as I promised to share content beyond interviews that would help you to actually build good LLD, I'd suggest you to read about common <a target="_blank" href="https://refactoring.guru/refactoring/smells">code smells</a> and their corresponding remedies.</p>
</li>
<li><p><strong>Common coding problems</strong> - Pick any playlist on YouTube and you'll find authors discussing around building your own small version of Uber, ZoomCar, BookMyShow, Splitwise and so on. I'll strongly suggest you to solve at least 5-7 problems on your own (without looking into solutions) before you appear for interviews. This exercise will make sure that you're able to apply the principles, patterns and product understanding to build a good low level design. I do not have a strong choice on the content but I'll <a target="_blank" href="https://www.youtube.com/watch?v=rliSgjoOFTs&amp;list=PL6W8uoQQ2c61X_9e6Net0WdYZidm7zooW">share this one</a> incase you want to get some idea on these problems.</p>
</li>
</ol>
<h2 id="heading-diving-deep">Diving deep</h2>
<p>I've given multiple interviews for different companies and positions. So I'll share what is expected from each level:</p>
<ol>
<li><p><strong>For Software Engineer/Sr. Software Engineer</strong> - The focus is more on machine coding. So your performance in Data structures and Low level design is judged more than anything.</p>
</li>
<li><p><strong>For Staff/Principal/EM roles</strong> - The focus is more on HLD (System design) and leadership qualities. Hands-on coding is evaluated less in comparison to HLD or in comparison to junior roles. You're just asked to share the schema design, API design, any challenging part of system that you need to discuss about.</p>
</li>
</ol>
<p>As an example, I was asked to write code for LLD of the <a target="_blank" href="https://github.com/gagan93jtg/lld-learning/tree/main/ruby/snakegame">Snake Game</a> in the Atlassian Interview process. As this was for a Senior Software Engineer role, the focus was on hands-on coding and they expected me to write completely working code within 45 minutes (plus some discussion around how I'd test this).</p>
<p>Whereas for Staff Engineer role, one of the LLD round did not involve coding at all. We majorly discussed the system entities (models), their relations, little bit on database design, API design and some specific parts of the system that sounded tricky (I call it the <strong>core problem</strong>). So it's very important to understand in the beginning what your evaluator is looking for. In most of the cases, the evaluator should clearly explain what they're looking for. But I've appeared for smaller companies also where this did not happen so I thought it is better to clarify rather than assuming. As a thumb rule, <strong>never assume anything in an interview</strong> (in any round). And if you do, share the same with your interviewer so that they can point out if that is a wrong assumption. Within a small period of 40-50 minutes, it's very costly to start with a wrong assumption and then correct it later.</p>
<p>I'll share a superset of what all things were asked from me in LLD round of different companies. Once the introductions are over, the interviewer gives you a problem statement. These are the things I take care of:</p>
<ol>
<li><p><strong>Making notes</strong>: I have a habit of taking down notes in all the technical interviews I give. I use a plain text editor window for the same (Sublime Text being my default choice for years). You could also use pen and paper but here the aim is that your interviewer should be able to read the same as you're writing. For the same, I also share my screen in the beginning. This helps me to scope down the problem statement before I start solving the specific parts.</p>
</li>
<li><p><strong>Figure out requirements</strong> - So my first task would be to understand what all parts of system we want to build. Take anything real world - Uber, ZoomCar or BookMyShow (let's use <strong>BMS</strong>). You cannot discuss their complete product within 45 minutes. So it's better that you figure out with your interviewer what they are interested in. For example, in BMS problem, they might be interested in the <strong>core booking problem</strong>, rather than discussing kind of seats in a movie hall. Similarly, while discussing a cab booking platform like Uber, the core problem could be <strong>driver allocation</strong> and not the <strong>inter city travel</strong> feature. Think from a product perspective here. If you have used the app, then you must be knowing about the core features (and maybe the core problem also) but incase you have not used the app, tell the same so that you both can work together to scope down the problem. Once this is done, there are two scenarios - Either they'll ask you to write the code or not.</p>
</li>
<li><p><strong>Coding required</strong> - Incase you need to write the code, start thinking about <strong>core entities</strong> (models), their <strong>interactions</strong>, <strong>relation</strong> with each other. Validate your understanding with the interviewer. If they're ok, start writing some code. Write the code in a top-down manner where you write the structure of everything first and leave some core algorithm implementation to a later time. Once the structure looks good, fill in the method implementations (based on available time).</p>
</li>
<li><p><strong>Coding not required</strong> - Incase they have not stated the expectations, talk to them to understand if you should be writing some code or just talk about APIs, models, and some core problem. If you're not required to write a working code, you should still mention the <strong>models</strong> and <strong>services</strong> because they are a starting point of your database design. In this case, do talk about detailed <strong>request and response formats</strong>. Some interviewers want you to talk about REST APIs and see your understanding around the same, specially if you're working on a API based problem (there are non-web problems also, discussed later). While talking about APIs, do state your assumptions (if any). For example - I assume that there is an authentication mechanism in place already and auth information is being passed in the headers for each of the APIs we're discussing.</p>
</li>
<li><p><strong>Database schema</strong> - Some interviewers can ask you to explicitly mention the specific tables and columns. In BMS, you'd have a <strong>city</strong> that has many <strong>theaters</strong>. Then a <strong>theater</strong> that has many <strong>movie halls</strong>. A hall can screen multiple <strong>shows</strong> during a day and has multiple <strong>seats</strong>. I hope you got an idea that highlighted words are our <strong>Models</strong>. So the interviewer would want you to write these tables, their columns (at least important ones) to see if you're designing the schema correctly. They can also ask you to write queries for specific APIs to see if your design can fulfil system requirements (eg. <em>Write a query to see if a Movie Show is completely sold out for a specific theater</em>).</p>
</li>
<li><p><strong>The core problem</strong> - Different systems can have different core problems. For example, if you're designing an app like <a target="_blank" href="https://github.com/gagan93jtg/lld-learning/tree/main/ruby/splitwise">Splitwise</a>, your core problem could be settling the amount within a group of people. For Uber, finding drivers and showing ETA could be discussed. Here, the interviewer can expect a mix of things from algorithms to design patterns based on the specific problem. As you dig deeper into the problem, there are chances that you'll figure out the core problem. Otherwise your interviewer may also give you a hint like: <em>I want you to talk about this problem</em>.</p>
</li>
<li><p><strong>Test cases/scenarios</strong> - One of expectations from a good developer is to figure out how can they test their code well. Incase you wrote code and have time left, you'll be asked to write some test cases. Otherwise you're expected to talk only around some test scenarios that you think are important. Nothing much to add here. If you know how to break your classes, follow design patterns and write test cases, that should suffice. If you think there is a part of problem in which some specific edge cases exist, do talk about it.</p>
</li>
</ol>
<h2 id="heading-conclusion">Conclusion</h2>
<p>So this is mostly what I've seen people asking during LLD interviews. One thing I'd like to add here - I saw three kinds of problems that are generally asked:</p>
<ol>
<li><p><strong>Simple Games</strong> based on some logic - Snakes game, snakes and ladders, Tic Tac Toe, Chess game, etc.</p>
</li>
<li><p><strong>Popular products</strong> - Cab booking (Uber), Car renting (Zoom Car), some booking platform (BookMyShow), etc.</p>
</li>
<li><p><strong>Technical systems</strong> - A rate limiter, Cache with LRU eviction, A PubSub System, Key-Value database, etc.</p>
</li>
</ol>
<p>The different kinds of problems have their own complexity. For example, it could be easy for you to discuss an app that you use a lot (like Uber) than discussing a PubSub system. I'd suggest you to spend time practising all these kinds of problems so that you get an idea on complexity of each of them. Although the general approach (as discussed above) would remain the same. Happy learning 😊.</p>
<hr />
<p>Thank you for reading this detailed post on Low Level Design. If you liked this, please have a look here also:</p>
<ol>
<li><p>I created a small <a target="_blank" href="https://github.com/gagan93jtg/lld-learning">github repo</a> that has solutions for four LLD problems in Ruby and Java languages. If you go to any of the problem, it will have a README that describes what we're building. You can take that README as a requirement to practice these problems. Refer my code only if you're stuck.</p>
</li>
<li><p>All my interview preparation/experience related posts are available <a target="_blank" href="https://blog.gagan93.me/tag/interview">here</a>.</p>
</li>
<li><p>I wrote a post last year about the <a target="_blank" href="https://blog.gagan93.me/classify-your-codebase">benefits of building small classes</a>, a practice I embraced years ago when I began refactoring systems.</p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Improving your Resume]]></title><description><![CDATA[Introduction
Hello people. I've you're reading this, you might be looking to build a better resume. It's important to keep your resume updated even if you're not looking out for a change. Sadly, not many people like me realise this on time. And becau...]]></description><link>https://blog.gagan93.me/improving-your-resume</link><guid isPermaLink="true">https://blog.gagan93.me/improving-your-resume</guid><category><![CDATA[resume-building]]></category><category><![CDATA[interview]]></category><dc:creator><![CDATA[Gagandeep Singh]]></dc:creator><pubDate>Mon, 13 May 2024 08:36:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/4YzrcDNcRVg/upload/e90878f10a5128c0b5a766f3f0cbb582.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Hello people. I've you're reading this, you might be looking to build a better resume. It's important to keep your resume updated even if you're not looking out for a change. Sadly, not many people like me realise this on time. And because of this, we end up doing major changes in our resume when we actually start looking out. For example, I started looking out from my previous job in late 2017 and this time in late 2023. In this six years, almost everything changed - from my role to market expectations.</p>
<h2 id="heading-the-versions">The versions</h2>
<p>Feel free to laugh but the 2017 resume looked like this (that got me into LocoNav). Before LocoNav, I applied to <a target="_blank" href="https://www.reddoorz.com/">RedDoorz</a> also and got a call from this resume. So probably it was good according to that time and my YOE.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1715584778871/67879bcd-ac61-45af-895e-49f7920fa008.png" alt class="image--center mx-auto" /></p>
<p>I'll not discuss a lot on this resume because I did not continue editing on this file. It needed a lot of restructuring, so rewrite was better. I'll share two more resumes based on my current skill set, the comparison between both. Even the first one was not that bad (rather it's fancy) as it got me a call from <a target="_blank" href="https://blog.gagan93.me/atlassian-interview-experience?">Atlassian</a> without a referral :)</p>
<p>The resume shared below was created around mid 2023:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1715585054786/30087692-90e2-4f80-9223-04ec04786e5a.png" alt class="image--center mx-auto" /></p>
<p>I thought fancy resume with photo would look cool 😶. I used some template on <a target="_blank" href="https://www.canva.com/">Canva</a> to build this. I shared this with 4-5 friends for a feedback. These were the points that I worked on:</p>
<ol>
<li><p>Build a simpler resume. That photo is not needed. Better start building on a simpler tool like Google Docs. I did that.</p>
</li>
<li><p>The two column resume is generally not extensible. For example, I had not mentioned a lot about my roles/responsibilities in the previous organisation. And once I added those details, the right section expanded to the second page where as left (blue-ish) section was empty.</p>
</li>
<li><p>Remove these things as much as possible, no one wants to read them:</p>
<ol>
<li><p>The top right part of resume [Objective] that starts with <em>Having about 8 years</em>.</p>
</li>
<li><p>Sections like Strenghts, weakness, languages, hobbies.</p>
</li>
</ol>
</li>
<li><p>I had made the <em>Language and Platforms</em> section too fancy by adding so many things, categorised under 4 sub-headings.</p>
</li>
<li><p>If the space is used nicely and we opt for a single column (plain Google Doc) resume, we could put the contact information in a single line (yes, I did that later).</p>
</li>
<li><p>Education section could be trimmed. Remove school/college name and just talk about degree and board/university. Again this can come in 3 lines if we go for a wider page (single column basically).</p>
</li>
<li><p>The most important point - The points mentioned in the resume do not talk enough about <strong>the impact</strong> I got into the projects. I majorly wrote like - <em>"I did this task single handedly"</em> or "<em>I contributed to this along with the team</em>". Our focus should be on the <strong>outcome</strong> or <strong>the impact.</strong> No one is interested to read plain stories that do not bring a good business or engineering impact.</p>
</li>
</ol>
<p>Apart from this, few improvements were done from my end also:</p>
<ol>
<li><p>Although I had done a lot of work in the past 6 years, it's important to choose the impactful work. The issue in my case was that I had taken up multiple things that were very impactful but I had to choose and still let go of few good things.</p>
</li>
<li><p>I had divided my role as a Staff Engineer into two parts - <strong>Enablement team and Payments team</strong>. That sounded like over-engineering the resume because for the reader, it might not make a lot of sense. Also, because the work on payment product had winded up, so I thought I should talk less about that and merge these sections. Moreover, I was working on a large internal product that was neither under payment, not enablement role, and I had to accommodate that as well. The best way was to merge a single "Staff Engineer" role and mention in points/sub-points.</p>
</li>
<li><p>I created second level category inside my resume wherever required. For example, for mentioning my work on the payments product, I thought it's better to mention a wider heading and explain everything under it, rather than putting each thing about payments product in a new point where this can get mixed with other points, or where I need to mention the context of "payments" in a repeated fashion. Because this was now a single column resume, creating nested categories was possible and was not wasting much space.</p>
</li>
<li><p>I tweaked font size, page margins, bullet margins and other settings in the Google Doc to ensure I'm not wasting space and I'm able to wrap everything in a single page. For example, I tweaked the indentation for nested bullets so that most of the nested points get completed within a single line.</p>
</li>
<li><p>Used ChatGPT to ensure no typos, grammar issues. It's also very helpful to write shorter sentences without losing the context. The only thing to see while using AI tools is that we need to make sure that overall language of resume is consistent. It shouldn't happen that some specific points are written in a very fancy English while others are too simple. Just take care to ensure that consistency.</p>
</li>
<li><p>Consider yourself as a problem solver (senior engineer). Don't associate yourself closely with a technology stack when you're talking about your contributions (eg. Java/Spring or Ruby/Rails or Python/Django). When you think in a framework-agnostic manner, you don't use names of libraries that are specific to your tech. This makes it easier for the reader to understand incase they don't share the same technology background. For example, Rails developers usually mention libraries like Sidekiq, ActiveAdmin, Devise and Rspec in their Resume. Now for someone who is filling a Java position, it will not be possible to understand these without googling. And resumes are hardly skimmed for a minute or less :) We should rather talk in generic terms like background processing framework, admin/backoffice library, authentication framework and testing framework. In this way, there are better chances of someone understanding your resume. Only place to mention your current language/framework is under the <strong>Skills section</strong>.</p>
</li>
</ol>
<h2 id="heading-the-result">The result</h2>
<p>This is my current resume that I have posted on my <a target="_blank" href="https://gagan93.me/">portfolio</a> also.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1715586787936/1d16409a-33ba-4442-8cde-9e1e8f01363b.png" alt class="image--center mx-auto" /></p>
<p>In comparison to the two column fancy resume, this is easier to modify because there were instances while editing the two column resume when I thought of adjusting the ratio of left and right side just because few points were crossing the line just by 1 word. Or I thought I should adjust the fonts. This one is much simpler and flexible to future modifications, just like a good software design :)</p>
<p>Again highlighting the most important point - talk of impact. I'll share a few examples on how I modified the points to show impact:</p>
<ol>
<li><p><strong>Before</strong>: <em>Took the first microservice out of a 6+ year old monolith, reducing a decent load from database</em><br /> <strong>After</strong>: ﻿﻿<em>Pioneered the transition of critical components to a microservices architecture, achieving a 75% speedup in the five slowest endpoints.</em></p>
</li>
<li><p><strong>Before</strong>: <em>Scaled Fastag payments product from 0 to 2.5Cr daily transaction amount. This is almost 2% of India's total daily toll.</em></p>
<p> <strong>After</strong>: ﻿﻿<em>Successfully scaled the system from inception to handle 2.5Cr daily transaction (2% of India's total toll volume).</em></p>
</li>
<li><p><strong>Before</strong>: ﻿﻿<em>Worked on reducing cloud spend by analyzing usage of servers and using other methods.</em></p>
<p> <strong>After</strong>:<em>Strategically reduced from 70+ to 5 queues based on latency/memory requirements, optimizing approximately 25% of infra costs.</em></p>
</li>
<li><p><strong>Before</strong>: ﻿﻿<em>Simplified how the main entity of system (vehicle) is accessed by various components.</em><br /> <strong>After</strong>: ﻿﻿<em>Planned and executed refactoring of core system entities, streamlining the access over 200+ reports and APIs.</em></p>
</li>
<li><p><strong>Before</strong>: ﻿﻿<em>Migrated Email provider to AWS SES with bounce/complaint list handling flow.</em><br /> <strong>After</strong>: ﻿﻿<em>Efficiently migrated ~ 150 different kinds of emails from Sparkpost to Amazon SES implementing bounce handling for increased reliability.</em></p>
</li>
<li><p>Removed points that could not be modified to show impact. Like:</p>
<ol>
<li><p><em>Involved in discussing overall global architecture of logistics based Saas Product.</em></p>
</li>
<li><p><em>Lead various other initiatives like removing huge chunks of dead code...</em></p>
</li>
</ol>
</li>
</ol>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In total, I had to do 5-6 iterations on my resume over a period of 9 months, based on feedback that I received from multiple people. The idea of this blog post <strong>is not</strong> to provide you <em>the best resume template on the internet</em> that will get you into your next FAANG role. I'm just sharing what worked for me and how I improved it over time. I've applied in about 6-7 companies where this got selected even without a referral. This includes both big tech and smaller companies.</p>
<p>Many of the points mentioned above are subjective, like:</p>
<ol>
<li><p>I've seen many senior people adding Objective / Career Summary on the top.</p>
</li>
<li><p>Or adding things like Strengths, Hobbies and Languages.</p>
</li>
</ol>
<p>And I do not mean that those resumes will get rejected. I just wanted to build an optimised resume which can be skimmed over efficiently and get me some interview calls. Some job portals allow you to write a separate cover letter where you can include details of your current role and expected role (overlaps somewhat with <strong>Objective</strong> section of the resume). At the end, it's not about <em>that one way of building it</em>, but for <em>what works for you</em>. Good luck for your Resume building journey.</p>
<hr />
<p>If you liked this post, please read these too:</p>
<ol>
<li><p>My <a target="_blank" href="https://blog.gagan93.me/stripe-interview-experience">Stripe Interview Experience</a>.</p>
</li>
<li><p>If you're working on reducing your cloud spend, do <a target="_blank" href="https://blog.gagan93.me/cloud-saas-cost-reduction">read this</a>.</p>
</li>
<li><p>I've shared a detailed case study where I optimised <a target="_blank" href="https://blog.gagan93.me/optimising-docker-builds">docker build time and size</a>.</p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Bain and Company Interview Experience]]></title><description><![CDATA[Background
I interviewed for the position of Staff Engineer II at Bain and Company in Jan-Feb 2024. They were hiring engineers for the Gurgaon team (that's a small team of <15 engineers). They had posted the job on LinkedIn, that redirected to their ...]]></description><link>https://blog.gagan93.me/bain-and-company-interview-experience</link><guid isPermaLink="true">https://blog.gagan93.me/bain-and-company-interview-experience</guid><category><![CDATA[bain]]></category><category><![CDATA[interview]]></category><category><![CDATA[staff-software-engineer]]></category><category><![CDATA[consultant]]></category><dc:creator><![CDATA[Gagandeep Singh]]></dc:creator><pubDate>Mon, 13 May 2024 05:36:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1715578883179/7326db42-ee15-4bcc-9402-9ad52041bd7f.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-background">Background</h2>
<p>I interviewed for the position of <strong>Staff Engineer II</strong> at <strong>Bain and Company</strong> in <strong>Jan-Feb 2024</strong>. They were hiring engineers for the <strong>Gurgaon team</strong> (that's a small team of &lt;15 engineers). They had posted the job on LinkedIn, that redirected to their website where I applied. For those who haven't heard much about the company:</p>
<ol>
<li><p>Popularly abbreviated as <a target="_blank" href="https://en.wikipedia.org/wiki/Big_Three_(management_consultancies)">MBB</a>, McKinsey, Boston Consulting Group and Bain &amp; Company are known as the Big three management consultant firms.</p>
</li>
<li><p>As the name suggests, these are mostly into consultancy and not in product development. And unlike other consultant firms where developers are outsourced by the company to build something, these big firms mostly "plan" the execution. Generally, the actual implementation (code) is not done by these companies. They just meet clients to understand the problem, document everything and make plans.</p>
</li>
<li><p>Most of the roles are <strong>Individual Contributor</strong> roles where you're expected to travel for 20-50% of your time (based on role/seniority). This is true for engineering roles as well.</p>
</li>
</ol>
<h2 id="heading-interview-process">Interview process</h2>
<p>I got an email from their recruitment team that they want to schedule a hiring manager call to begin with. Except for one instance, entire communication happened over emails and the team was quite responsive. There were <strong>five rounds</strong> in total. I cleared all of them but because there was a single vacancy and high competition, I was informed that they chose someone else. Overall, it was a good learning experience as this was the first time I went through all the rounds for any company's process.</p>
<h3 id="heading-hiring-manager-round">Hiring manager round</h3>
<p>This was scheduled with Sr. Director of Global Engineering team who was based out of Singapore. There are many companies that put you through the evaluation process directly but I like the organisations where we have a human interaction with a senior person before starting with the interview rounds. They make sure that they understand your current role, future growth path/expectations and also explain the new role in the best possible way. For example, most of us work with product/service based companies where travel is almost negligible for engineers. This was very different for Bain. Another thing was that we get to code less in consultant roles. He made sure that I completely understand these two things before starting with the evaluation process. He sounded calm and "not in a hurry" like many directors/senior managers. We did some technical discussion around my projects but only on surface level.</p>
<h3 id="heading-live-coding-round">Live coding round</h3>
<p>This was scheduled with a <strong>Senior Engineer</strong> who just got promoted to <strong>Staff</strong>. Generally, when you talk to Engineers during interviews, you might find them less expressive but people at Bain are much more open (and somewhat more professional) when it comes to communication. This is because Engineers also deal with the clients and are expected/trained to be good on communication skills. A junior engineer also joined the interview as an observer. He dropped mid way due to a clashing meeting. The problem statement was based on strings and 1D arrays (quite simple, but I don't remember exactly). While the problem was simple to solve, the platform they chose was not good. Most of the coding interviews I gave were on platforms like Coderbyte that has a wide choice of languages. Being a Ruby Engineer, I prefer to solve DS questions in <strong>Ruby</strong> but here, the choice was among <strong>Python, Java and Javascript</strong>. This was not communicated by HR team in advance. Somehow I ended up coding the solution in Java, which is my second language of choice, and that ran without any issues. I expected another question but the interviewer preferred to play around with minor changes in requirements and edge cases, all of which were passing. This sounded easier than I expected and what I faced in other companies.</p>
<h3 id="heading-technical-assessment">Technical Assessment</h3>
<p>This was basically a <strong>System Design / HLD round</strong> with another <strong>Staff Engineer II.</strong> I prefer interviews during morning hours because I'm fresh at that time. But because of interviewer's preference/availability, this was scheduled in the evening. I'm generally mentally-saturated by that time (6PM IST). The problem statement was around designing a <strong>blob</strong> <strong>storage platform</strong> like <strong>Dropbox / Google Drive</strong>. Although this is a common HLD problem, I had not solved it before. The only way was to think from first principles. I'm good at cloud related things and protocols so that never remains an issue for me when I'm solving a HLD problem. I'm also good at databases but my major exposure is around SQL databases only. Considering the numbers shared by them, I tried to propose a NoSQL database for the metadata storage. They immediately pointed this out and asked me why I chose a NoSQL database here. When I started pointing out the reasons, I figured out that I was not having strong reasons to go for NoSQL. Within next two minutes, both of us mutually agreed to start with a SQL database like PostgreSQL. The major discussion in a blob storage backend (from a functional perspective) is to think about:</p>
<ol>
<li><p>Breaking file(s) into chunks for both uploading/downloading.</p>
</li>
<li><p>Hashing these parts to see if it already exists on the backend (can happen frequently for large platforms or when people upload duplicate files).</p>
</li>
<li><p>Encryption/Decryption on client/server side (this was one of the requirements).</p>
</li>
<li><p>Talk about Disk, storage, cost, etc.</p>
</li>
</ol>
<p>I had read read an article on <a target="_blank" href="https://www.allthingsdistributed.com/2023/07/building-and-operating-a-pretty-big-storage-system.html">Amazon S3</a> (not full, it's a long one for me to absorb). I took some ideas from it when talking about low level storage. Although S3 is much more complex and I was not supposed to talk about things like storage tiers. We didn't conclude our discussion around all the points (can happen in a HLD round, that is more open ended in comparison to coding rounds) but he asked me to talk around APIs, schema design and draw some boxes on <a target="_blank" href="https://excalidraw.com/">Excalidraw</a> (my choice for drawing diagrams) before the time ends. The overall discussion went very well. Hence, I qualified.</p>
<h3 id="heading-technical-assessment-case-study">Technical Assessment (Case Study)</h3>
<p>This was another HLD round scheduled with <strong>Director of Engineering, India team</strong>. He wanted to understand about my projects in great depth and probe around choices. Then we moved to designing an app that we regularly use - like <strong>Zomato</strong>. He asked some problems specific to:</p>
<ol>
<li><p>How we'll show restaurants/branches near customer's current location?</p>
</li>
<li><p>What tables will you make to store restaurants, branches, etc?</p>
</li>
<li><p>How would you build Zomato's homepage (gave some requirements, similar to the actual app)? Write SQL queries and see what things should be cached for fast page load.</p>
</li>
</ol>
<p>While the previous round was centered around a single problem (Blob storage), this included my projects, a case study, discussion around databases, discussion around other technology choices, and a lisuttle bit on the product side.</p>
<p>--<br />After this round, I got a phone call from the recruiter (for the first time). They gave me feedback on all my previous rounds which was very positive. The only negative she pointed out that I sounded comfortable with only Ruby programming language. She concluded this because I had mailed them about the platform issue in live coding round that did not have sufficient language options to choose from. I explained the same and I told that I was comfortable enough with Java to solve the problem within time. I again explained that I consider myself as a <em>Sr. Engineer</em>, and not a <em>Ruby Engineer</em> but their platform should have had more options available. Otherwise they should inform the same before the interview. It was important for them to clear this expectation from their side, as for client projects languages keep on changing. Moreover, most of their enterprise clients were using Java/C#.</p>
<p>They told me that next round is with an Expert Partner. This will be a Behavioural Round (kind of cultural fit round). Because the partner is generally travelling, I'll have to provide my availability over multiple slots in a week and they'll schedule in any of these slots.</p>
<h3 id="heading-behavioural-round">Behavioural Round</h3>
<p>As explained, this was held with an <strong>Expert Partner</strong> from <strong>APAC region</strong>. The interview was scheduled for 45 minutes but in the beginning they told me that they just have 30 minutes because of a last minute meeting setup with a client. This was the first time I was giving Behavioural Round in my life so I had spent considerable time in understanding what this round is meant for. Obviously, I couldn't fool this person if I was not a cultural fit because they had 21 years of experience and 16+ years within Bain. But I said to myself - If I fail in this round after clearing 4 technical rounds, this would be the worst thing. I checked a lot of videos to learn about the STAR approach, making stories out of things you've done in your career. There was one short video that suggested to build stories around (at least) these axes:</p>
<ol>
<li><p>Stories that indicate good leadership,</p>
</li>
<li><p>Where you acted as a great team player,</p>
</li>
<li><p>Accomplishment story to show that you can tick off your goals,</p>
</li>
<li><p>Overcoming hurdles to show that you can surpass challenges,</p>
</li>
<li><p>Failure story to show that you learn from your mistakes and bounce back.</p>
</li>
</ol>
<p>When I say “stories”, don’t take me wrong. It doesn’t mean creating fake stories. Because the interviews are structured in a way that you’re evaluated on a lot of things in a short interval, it’s good to know the axes on which you should prepare before-hand. Otherwise you might have showcased all the skills but you’d still forget those when you’re asked during an interview.</p>
<p>The interview started with our introductions. He talked a little bit around Logistics business (i.e. around LocoNav). Then they said “<em>You’ve worked at LocoNav for 6 years, can you explain me one big task that you completed each year</em>”. This was the moment when all the preparation felt useless. While I’ve done a lot of work at LocoNav and everybody around me knows that, I felt blank for 2-3 seconds. After that I started explaining my contributions. There were a few more questions that were no where related to what I had expected. At the end, they asked if I had questions for them. I asked a few questions and we wrapped with the interview.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>The interview at Bain took slightly longer than other companies. The overall process took close to 2 months as rounds happened within a gap of 1-2 weeks, specially for the last round where availability of Expert Partner was very limited. Even after the last round, they took 2-3 weeks before sharing the result. I was informed that there was <strong>only one opening</strong> for this position and they shortlisted some other candidate. I probed for more details during the month of March because this feedback seemed like a generic <em>platform generated email</em>. The HR added the hiring manager to the email and they both confirmed the same. It sounded somewhat like:</p>
<blockquote>
<p>From HR:</p>
<p>Thank you for reaching out. We had multiple candidates, including yourself, who progressed to the final stages of the interview. It's important to note that while you demonstrated strong skills and abilities, the candidate who ultimately succeeded in the final round performed exceptionally well. This outcome doesn't reflect any deficiency in your skills; rather, it's a testament to the competitive nature of the final selection process.</p>
<p>--</p>
<p>From the Hiring Manager:</p>
<p>Thanks for reaching out. Yes, we liked your candidature and got good feedback from the interviewers. Unfortunately we only had one open position at this point of time and had a tough decision to select one candidate. We will surely reach out to you if we open more positions in the future.</p>
<p>Once again, really appreciate the time and effort you spent for the interview process with us.</p>
</blockquote>
<p>I got some relief after this email, because I believe feedbacks must be shared when a candidate spends time with you. Including all the emails, the phone call and the interview rounds - I spent more than 6 hours with them. I read a lot about consultant life during the interview process to see if I can work like that (because I'm not a travel freak). I might have given a chance to this role if I was selected. I also thought - will I stay relevant in the <em>SaaS Product industry</em> if I opt for this role for a few years (that doesn't involve hands-on coding)? But whatever happens, happens for the best. I got the complete experience but I was not selected. I was overall happy.</p>
<hr />
<p>If you liked reading this, please read my other interview experiences and tech blogs:</p>
<ol>
<li><p><a target="_blank" href="https://blog.gagan93.me/atlassian-interview-experience">Atlassian experience</a>, <a target="_blank" href="https://blog.gagan93.me/stripe-interview-experience">Stripe experience</a> and a short post around <a target="_blank" href="https://blog.gagan93.me/titles-inflation-and-down-levelling">job titles</a>.</p>
</li>
<li><p>Today's SaaS products uses a lot of services from cloud providers that locks them into one provider. Read about <a target="_blank" href="https://blog.gagan93.me/cloud-pricing-vendor-lock-ins">vendor lock-ins in cloud</a>.</p>
</li>
<li><p>More than building a large system, it's important to <a target="_blank" href="https://blog.gagan93.me/avoid-redundant-complexity">build it simple</a>.</p>
</li>
<li><p>Propagate a culture of asking questions in your team. Learn to <a target="_blank" href="https://blog.gagan93.me/asking-good-questions">ask good questions</a>.</p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Stripe Interview Experience]]></title><description><![CDATA[Background
I interviewed for the position of Staff Software Engineer at Stripe in December 2023. They were hiring engineers in the Verification & Identity Platform team. I applied through a referral as that increases your chances of getting shortlist...]]></description><link>https://blog.gagan93.me/stripe-interview-experience</link><guid isPermaLink="true">https://blog.gagan93.me/stripe-interview-experience</guid><category><![CDATA[staff-software-engineer]]></category><category><![CDATA[stripe]]></category><category><![CDATA[interview]]></category><dc:creator><![CDATA[Gagandeep Singh]]></dc:creator><pubDate>Tue, 07 May 2024 11:21:15 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1715070334117/78cbabca-ab87-4131-ae60-a3d78588bfee.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-background">Background</h2>
<p>I interviewed for the position of <strong>Staff Software Engineer</strong> at <strong>Stripe</strong> in <strong>December 2023</strong>. They were hiring engineers in the <strong>Verification &amp; Identity Platform team</strong>. I applied through a referral as that increases your chances of getting shortlisted. For those who haven't heard much about Stripe:</p>
<ol>
<li><p>It is one of the big players in <a target="_blank" href="https://docs.stripe.com/products">global payment products</a>.</p>
</li>
<li><p>In the past, Stripe has been known to be the <a target="_blank" href="https://www.linkedin.com/posts/gergelyorosz_stripe-softwareengineering-activity-7140426202174967808-xDQE/">largest Ruby codebase</a> in the world. They used Sinatra (not Rails) for web apps, so they're not the largest Rails codebase (which <a target="_blank" href="https://shopify.engineering/shopify-monolith#:~:text=Our%20main%20monolith%20is%20one,of%20developers%20currently%20adding%20features.">Shopify is</a>).</p>
</li>
<li><p>They have a wide tech stack now but their founder explains <a target="_blank" href="https://www.quora.com/Why-did-Stripe-choose-to-use-Ruby-for-its-backend-language">why they used Ruby</a> to start with. Stripe also has a very <a target="_blank" href="https://stripe.com/blog/engineering">popular technical blog</a>.</p>
</li>
</ol>
<p><strong>Note</strong>: Stripe asks the candidates to sign a <a target="_blank" href="https://news.ycombinator.com/item?id=1952848">NDA</a> because of which I cannot share the exact interview questions but I'll still share the format of interview and difficulty of questions to some extent.</p>
<h2 id="heading-interview-rounds">Interview rounds</h2>
<ol>
<li><p><strong>Telephonic round with Recruiting team</strong> - This was scheduled to understand my role, projects, career goals and set up alignment with the role. The recruiter was based out of Singapore and was taking care of APAC hiring. Although they were not technical, they were expert in explaining the job description and were clearly able to point out things that I was not doing right now in my job role (that they needed for the role). For example, I work as a Staff Engineer at LocoNav which is a small team. So here, my role doesn't involve regularly setting up technical direction for the team but considering Stripe's scale, Staff Engineers are very senior in role and they take care of the same on regular basis. So, they told me before hand that based on my performance, I might be <strong>down-levelled</strong> if they see a need to do this. This could sound discouraging as the position below <strong>Staff Engineer</strong> was of <strong>Software Engineer</strong> but they explained that engineers with the experience range of 0-15 years of experience do fall under this role. This is so because Staff Engineers are very senior in Stripe and they do not have a middle role (i.e. Senior Software Engineer).<br /> On a side note, I wrote about <a target="_blank" href="https://blog.gagan93.me/titles-inflation-and-down-levelling">titles and down-levelling</a> incase you want to understand when it is good or bad for you. Additionally, although the JD mentioned the location as <strong>Remote</strong>, the recruiter clarified that they're planning to start WFO soon (early 2024) so the location would be Hyderabad/Bangalore.</p>
</li>
<li><p><strong>System Design Round</strong> - Staff Engineers at Stripe typically go through <strong>two</strong> System Design rounds. In each round, there is a Staff Engineer who evaluates your design. In this round, the interviewer was from a different timezone and a very senior person (~10 years senior to me). They explained the problem statement and waited for me to probe for details. While many companies ask for the design of well-known large systems (eg. Youtube, Whatsapp, Google Drive, etc), Stripe is known to ask questions around their own systems only. So the question was related to payments product. This was first ever System Design Interview that I was giving in my career, so I was skeptical on my approach. The problem statement seemed very simple and I queried 2-3 times (like "is <em>this is what I have to build?").</em> At that time, I was not familiar with the fact that simple problems can also be asked for senior roles, but the expectation of solution is with respect to candidate's expertise/seniority. Just to explain this more - You might have heard that some companies (like Atlassian) have High level design (or System Design) round even for hiring college freshers. At their level, the expectation is that they should understand basic building blocks of the overall system and have some idea on non-functional requirements. The same problem statement can be given to a Senior Engineer where the expectations will be very different. The interviewer can probe into specifics of protocols, choice of databases and so on. For Staff/Principal/Distinguished levels, the discussion can go towards privacy, compliance, regional failovers, disaster recovery etc. I started attempting the question and explained the components of the system and some APIs. I missed one thing around scale of data that the interviewer pointed out. At this point, I realised that exposing an API endpoint is not a good idea and we should have a queueing mechanism like Kafka in between for improved reliability at scale (eg. if consumer system crashes, it can retry from where it left on the Topic).<br /> Because this was my first ever System Design interview, I was a bit confused during my attempt and the interviewer sensed that. They shared the feedback with the recruiting team that I'm a good candidate but probably I should be considered for a lower level (i.e. Software Engineer). The same was conveyed to me and I agreed to be evaluated again for SE role. The interview process restarted.</p>
</li>
<li><p><strong>Coding round</strong> - As per the documents shared with me, Stripe's interview format claims to be very different than big tech companies as they have rounds where you engage more with the interviewer to solve problems and debug issues. For example, their interview process mentions some rounds where you have a codebase ready in which you implement a specific functionality or solve a bug that they've intentionally added. Surprisingly, this round was nothing like that and I was presented with a typical data structures problem 😶. It was a simple problem, again related to their products (i.e. payments). I asked my doubts and implemented the solution that worked. The interviewer modified the problem statement and asked me to accommodate the same in my solution. This one took some time but I was still able to solve it. With this, the interview came to an end and he asked me if I have questions. I asked how many more variations exist in the problem, to which he said "total of five". After the interview, I searched for the problem and found that this question was a typical Google's telephonic interview round question (not one "related to their products") 😲.</p>
</li>
</ol>
<p>After a few days, I was informed through email that I did not clear the round. I thought - maybe I was expected to solve more variations of the problem within the given time because my solution was working for the given cases. As usual, I thought I'd reach out to the recruiter to seek the exact feedback but couldn't do so because they were on new year vacation. They asked me to schedule a slot on their calendar that never loaded on my browser. Even today if I open the link, it keeps loading like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1715069474800/260468bf-48fe-478b-96a4-3cc208dbf610.png" alt class="image--center mx-auto" /></p>
<p>LOL. Not blaming it on them. They might have responded if I had mailed them back with the loading issue, but I lost touch due to their vacations and never contacted them again.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>It was a good experience with Stripe team. The recruiter was responsive on call/chat/email throughout and ensured timely response. I like their <a target="_blank" href="https://newsletter.pragmaticengineer.com/p/stripe-part-2">culture of writing</a> that <a target="_blank" href="https://www.linkedin.com/posts/gagan93_culture-writing-team-activity-7170322475212374016-UjEc?utm_source=share&amp;utm_medium=member_desktop">starts from their CTO</a> - something that everyone should do (if they can). I would have performed better in System Design Interview if this was not my first ever HLD interview but no regrets. Failures are integral to the journey of success 📈.</p>
<hr />
<p>If you liked reading this, I'd suggest a few more:</p>
<ol>
<li><p>If you're switching from your role, you might want to understand <a target="_blank" href="https://blog.gagan93.me/titles-inflation-and-down-levelling">how levels work</a> across companies.</p>
</li>
<li><p>My recently wrote about my <a target="_blank" href="https://blog.gagan93.me/atlassian-interview-experience">Atlassian Interview Experience</a> for the position of Senior Software Engineer.</p>
</li>
<li><p>If you work remotely, then make sure you're doing it <a target="_blank" href="https://blog.gagan93.me/efficient-remote-teams">efficiently</a>. I've compiled the best of what I learnt by working remotely in the past 4+ years.</p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Atlassian Interview Experience]]></title><description><![CDATA[Background
I interviewed for the position of Senior Software Engineer at Atlassian in November 2023. This was my first ever experience getting interviewed by a large product organisation. Just to brief about myself and my background - I currently wor...]]></description><link>https://blog.gagan93.me/atlassian-interview-experience</link><guid isPermaLink="true">https://blog.gagan93.me/atlassian-interview-experience</guid><category><![CDATA[atlassian]]></category><category><![CDATA[interview]]></category><category><![CDATA[senior-software-engineer]]></category><dc:creator><![CDATA[Gagandeep Singh]]></dc:creator><pubDate>Sat, 04 May 2024 12:54:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1714827471773/eab6cbcb-a8c8-4b9f-9bc3-623cfa651207.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-background">Background</h2>
<p>I interviewed for the position of <strong>Senior Software Engineer</strong> at <strong>Atlassian</strong> in <strong>November 2023</strong>. This was my first ever experience getting interviewed by a large product organisation. Just to brief about myself and my background - I currently work as a Staff Software Engineer at LocoNav India (2018-present) and have previously worked with Josh Technology Group (2015-2018). You can know more about me <a target="_blank" href="https://gagan93.me/">here</a>. I was not actively looking for a change but thought of giving a try. Having spent ~ 6 years at LocoNav (as of that time), I was completely out of touch with the current market expectations and interview process. Atlassian, just like many other big tech companies takes candidate experience very seriously. So their HR contacted me and explained everything on a 30 minute phone call including my salary expectations. They did not ask my current compensation though (something most of the companies ask). After our call, he sent me a few emails that summarized whatever he explained and had details about the interview process. Apart from this, the email also contained some documents to assist me in preparing (eg. telling what all things I should go through). Overall, it was a very good experience with them.</p>
<h2 id="heading-the-rounds">The rounds</h2>
<p>Atlassian was hiring aggressively in past 6-8 months for multiple positions (Senior/Principal/Fullstack) and rounds are more or less same for these positions, except 1-2 extra rounds for Principal Engineers. The rounds were as follows:</p>
<ol>
<li><p><a target="_blank" href="https://karat.com/">Karat</a> screening round (coding + Rapid QnA)</p>
</li>
<li><p>Data structures round (coding)</p>
</li>
<li><p>Low Level Design round (coding)</p>
</li>
<li><p>System Design round (diagrammatic mostly)</p>
</li>
<li><p>Values round (discussion)</p>
</li>
<li><p>Management round (discussion)</p>
</li>
</ol>
<p>The same is documented <a target="_blank" href="https://online.flippingbook.com/view/325478/4/">here</a> as a reference for candidates.</p>
<h2 id="heading-round-details">Round details</h2>
<p><strong>Karat Screening Round</strong></p>
<p>Karat is an online platform used by companies like Atlassian, to filter candidates after shortlisting the resume. The interviewers are Senior Engineers who are trained for many hours before they start taking interviews. As they have a global pool of interviewers, you can choose any time slot (unlike for other rounds) and they'll arrange an interviewer for you. My interviewer was an Indian from Hyderabad or Bangalore (don't remember exact location). He was very professional and friendly. Because this interview had to cover a lot of things in an hour, he made sure that we do not waste time discussing something specific at length. I remember him introducing himself within 15-20 seconds and then asking me to introduce myself in a minute or less. The format of the round was as follows:</p>
<ol>
<li><p>Introduction and explanation of format - 3-4 mins</p>
</li>
<li><p>5 Rapid fire system design problems (debugging rather than diagramming) - 20 minutes</p>
</li>
<li><p>Two data structure problems (easy, medium) - 30 mins</p>
</li>
</ol>
<p>Because this round was not with a company employee, you do not ask questions about the company in this round. We started with rapid fire system design problems. Here, we do not solve one large problem (like designing Spotify or Google Drive). Rather, we are presented with situations where experience is required to debug a system that is facing problems. I found this much better than the actual System Design round that all the companies take because only practical knowledge can help in debugging such problems. There were five questions, out of which I answered 4 correctly (the passing percentage was 60%, i.e. 3 questions). Just to share a few of them:</p>
<ol>
<li><p>A system is running very slow (+ some more details about the system). How would you debug this to solve the slowness.</p>
</li>
<li><p>A system is scaling from x to y users (x and y are number of users. What things would you consider to upgrade in your system to ensure smooth scaling?</p>
</li>
<li><p>An electronic system exists for a smart freezer. An external computer system does a processing right now but later we’re assuming to migrate this code to an internal microcontroller (raspberry pi for example). What things to consider while porting this code?</p>
</li>
</ol>
<p>These questions were easy for me because I had seen systems growing during my role at LocoNav. So I knew the pain points. In this round, the challenge lies in quickly understanding the problem and sharing all potential solutions within 3-4 minutes. With 5 questions to address in just 20 minutes, this means you have approximately 4 minutes per question, including the time taken by them to read out each question. To optimise your chances of success, the Atlassian Talent team recommends requesting the interviewer to refrain from reading the questions aloud, allowing you to read them at your own pace and save valuable time.</p>
<p>After this, we moved to the data structure problems where the first question was around string manipulation. They use <a target="_blank" href="https://coderbyte.com/">Coderbyte</a> platform to asses the coding part. I chose Ruby language to solve data structures in all the coding rounds I have given because I was most comfortable in Ruby. Unlike Leetcode, the test cases are not large in number and you're just expected to pass 5-7 test cases. I don't remember the exact problem but I remember that it was an easy one and related to Strings. One thing I remember is that I told wrong complexity when I was asked for the first time, and within 2-3 seconds I figured out and corrected myself. The second question sounded medium level leetcode problem but there was not much time left so I just explained the logic that sounded okayish to him and we concluded.</p>
<p>I had a chat with my recruiter who told me that I should have qualified as solving one DS problem was the criteria and I had also explained the logic for second one. One bad part here was that he ghosted me for about 2 weeks after this round (didn't pick my calls or responded to chat).</p>
<p><strong>Data Structures Round</strong></p>
<p>After 2 weeks, I got a call from another recruiter within Atlassian who told me that she'll be taking my candidature forward. She explained that the next two rounds (Data structures and Low level design) would happen on the same day, with a difference of 30-60 minutes in between. Both the rounds were for about 45 minutes (although marked for 1h on Calendar). The data structures round was taken by a Senior Software Engineer who was working with Atlassian Bangalore team for about 2 years. Like the previous round, this guy was also very professional and polite. He explained the problem and started discussing the solution with me. The problem was similar to what is explained <a target="_blank" href="https://leetcode.com/discuss/interview-experience/1739628/atlassian-sse-remote-reject">here</a> (don't go into his solution, just read the problem). There were multiple issues at my end due to which I didn't move forward from this round:</p>
<ol>
<li><p>The question wasn't a hard one but I was much less prepared.</p>
</li>
<li><p>While most of the data structure problems are simple to implement with a single method (or maybe 2-3 methods for readability), this problem required classes to solve because he was changed the question twice after initial problem statement and that was only solvable cleanly if we had written a custom comparator in the class (basically tie breaking logic in the comparator).</p>
</li>
<li><p>It seemed like the round mixed DS and OOPs (low level) slightly but that's fine. He gave me hints after which I changed my approach, but probably that was a negative point that lead to rejection despite solving it. My implementation:</p>
</li>
</ol>
<pre><code class="lang-ruby"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Ballot</span></span>
  <span class="hljs-keyword">attr_accessor</span> <span class="hljs-symbol">:candidates</span>

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">initialize</span><span class="hljs-params">(array)</span></span>
    <span class="hljs-keyword">self</span>.candidates = []
    array.each_with_index <span class="hljs-keyword">do</span> <span class="hljs-params">|candidate, i|</span>
      candidates &lt;&lt; BallotUnit.new(candidate, <span class="hljs-number">3</span> - i)
    <span class="hljs-keyword">end</span>
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BallotUnit</span></span>
  <span class="hljs-keyword">attr_accessor</span> <span class="hljs-symbol">:candidate_name</span>, <span class="hljs-symbol">:weightage</span>

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">initialize</span><span class="hljs-params">(candidate_name, weightage)</span></span>
    <span class="hljs-keyword">self</span>.candidate_name = candidate_name
    <span class="hljs-keyword">self</span>.weightage = weightage
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Candidate</span></span>
  <span class="hljs-keyword">include</span> Comparable

  <span class="hljs-keyword">attr_accessor</span> <span class="hljs-symbol">:weightages</span>, <span class="hljs-symbol">:name</span>

  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">initialize</span><span class="hljs-params">(name)</span></span>
    <span class="hljs-keyword">self</span>.name = name
    <span class="hljs-keyword">self</span>.weightages = []
  <span class="hljs-keyword">end</span>

  <span class="hljs-comment"># override the comparator</span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">&lt;=&gt;</span><span class="hljs-params">(second_object)</span></span>
    -(weightages.sum &lt;=&gt; second_object.weightages.sum)
  <span class="hljs-keyword">end</span>

<span class="hljs-keyword">end</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Runner</span></span>
  <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_results</span><span class="hljs-params">(ballots)</span></span>
    candidates_map = Hash.new

    ballots.each <span class="hljs-keyword">do</span> <span class="hljs-params">|ballot|</span>
      ballot.candidates.each <span class="hljs-keyword">do</span> <span class="hljs-params">|candidate|</span>
        <span class="hljs-keyword">if</span> candidates_map[candidate.candidate_name]
          candidate_object = candidates_map[candidate.candidate_name]
        <span class="hljs-keyword">else</span>
          candidates_map[candidate.candidate_name] = Candidate.new(candidate.candidate_name)
          candidate_object = candidates_map[candidate.candidate_name]
        <span class="hljs-keyword">end</span>

        candidate_object.weightages &lt;&lt; candidate.weightage
      <span class="hljs-keyword">end</span>
    <span class="hljs-keyword">end</span>

    candidates_map.sort_by {<span class="hljs-params">|k, v|</span> v }
  <span class="hljs-keyword">end</span>
<span class="hljs-keyword">end</span>

ballots = [
  Ballot.new([<span class="hljs-string">"A"</span>, <span class="hljs-string">"B"</span>, <span class="hljs-string">"C"</span>]),
  Ballot.new([<span class="hljs-string">"A"</span>, <span class="hljs-string">"C"</span>, <span class="hljs-string">"D"</span>]),
  Ballot.new([<span class="hljs-string">"B"</span>, <span class="hljs-string">"D"</span>, <span class="hljs-string">"C"</span>]),
]
r = Runner.new
puts r.get_results(ballots)
</code></pre>
<p><strong>Low Level Design Round</strong></p>
<p>In this round, I was asked to write complete logic for the <a target="_blank" href="https://www.google.com/search?sca_esv=58ac0b011cee9c7d&amp;sca_upv=1&amp;sxsrf=ADLYWILxVaKlInE0xUo1vWu1XUaU7ZPEjw:1714822721076&amp;q=nokia+snake+game&amp;uds=ADvngMjdw7-lOP0usjsMlKT3CLljzP00Ul4oFLKOAj9fGL-X8YrXHqfKk07DE1nxWsVjTChlHiOSf6mINDYeCaoPC80kWZL5frydgbHBTjR1-hBeX8_9mae0Jo4tFKDauOj2WZof5WWOTBGjTgfTRsJxhNX76UcXMq2fdnNIDy4XpwoCyddzgAaVdeFZqk8KxQNmT1nBUEup6WozpKo7AidCFu94Pjsc6hAHYEeOUzUUIxciXWD8hb2cykGoSwB7t1TBJoK9aab2T6YSSaZaRtKMkRFsxgudAzOnpozTwyj0JRoV9gxKM_zQwzRvBpfneq80yfWSeQP4&amp;udm=2&amp;prmd=isvnmbtz&amp;sa=X&amp;ved=2ahUKEwjkt5G59POFAxWtd2wGHZxpAXEQtKgLegQIEBAB&amp;biw=1496&amp;bih=812&amp;dpr=2#vhid=vrfGi0KpOSUCsM&amp;vssid=mosaic">snake game</a>. The panel comprised of a Senior Software Engineer and a Software Engineer (as a shadower) from the Sydney team. Both were old members of the team. I was somewhat surprised to hear this question because I had read that this was asked by them in 2022 and I thought they won't repeat it. The miss on my side is that I didn't practice this at all, so it took some time to understand the requirement and start. The expectation was that I'll write a completely working code and write test cases (or at least discuss the edge cases) within 45 minutes. I later implemented the code in both <a target="_blank" href="https://github.com/gagan93jtg/lld-learning/tree/main/ruby/snakegame">Ruby</a> and <a target="_blank" href="https://github.com/gagan93jtg/lld-learning/tree/main/java/snakegame">Java</a> along with test cases and found that it was quite hard to complete this within the expected time frame. You can read the requirements (Readme in both the links) and the code as well. Do read test cases to understand what scenarios I've covered.</p>
<h2 id="heading-result">Result</h2>
<p>I was skeptical of my performance in these DS and LLD rounds and was quite sure that I won't easily pass these. Given the market conditions of Dec 2023 (and even today) where the supply of good engineers is much more than the demand, I was sure that either they'd reject me or will do the same round again. I called the recruiter after a few days who told me that they're not carrying my candidate forward. She was very transparent to read out feedback of both the interviewers on the call. I noted those points to improve upon them before giving further interviews.</p>
<h2 id="heading-the-good-and-bad">The Good and Bad</h2>
<p>The interviews these days do not test if someone can solve a genuine problem within an hour. They basically test if you can solve fast enough, and that is only possible by practising for days and months. I started my first job in 2015, and since then I had not formally prepared for any interview. The recruitment process at LocoNav was not very formal as I joined among the first 5-6 engineers. And having spent 6+ years at LocoNav, I was completely out of touch with the market expectations. Sharing a few good parts and mistakes that I did starting with good parts:</p>
<ol>
<li><p>The interviewer saw my profile on Instahyre and reached out to me. As I knew that I'm completely unprepared, I took 2 weeks of time for preparation even before the Karat round.</p>
</li>
<li><p>I went through all the docs shared by him religiously and asked more questions to understand each round better.</p>
</li>
<li><p>After years, I started practising data structure problems and solved about 30-40 problems before my first round (mostly easy, medium).</p>
</li>
<li><p>My practical experience at LocoNav eased the preparation for LLD and HLD rounds (although I didn't attain the required speed because that needs more practice).</p>
</li>
</ol>
<p>Now coming to some bad parts:</p>
<ol>
<li><p>I didn't spend time in checking existing questions asked by them. Later I googled more and found that Atlassian is one of the companies that is known to repeat questions many times. One should always spend time in checking previously asked questions.</p>
</li>
<li><p>I should have taken about a month to prepare because my practice was not enough. I was shy to ask for more time.</p>
</li>
</ol>
<h2 id="heading-conclusion">Conclusion</h2>
<p><em>Was I discouraged by the rejection</em>? Yes (for a few days). But I applied in more companies and faced more rejections that helped in building my confidence. You might feel like you have practised enough, but that 1 hour of time with the interviewer is very different. The mental pressure leads to drop in confidence due to which you go in wrong direction and end up wasting time. The only way to solve this is to give more interviews and learn to be comfortable.</p>
<p>Atlassian was not my dream company but I have been using JIRA and confluence for years and I admire their products. I'm a JIRA administrator at LocoNav and my exposure has been far beyond the typical developer's usage. The good point about Atlassian was a remote role with handsome compensation. It is known to offer perks similar to FAANG companies (check their Bangalore office). But the bad part (heard recently) is that they revoked PPO for freshers and <a target="_blank" href="https://www.teamblind.com/search/Atlassian">have also been putting many engineers on PIP recently</a>. Looks like they've over-hired like many other companies :) Another thing that seemed off to me was that I got in touch with multiple people working at Atlassian (including the Data structures round interviewer) and all of them were working on the same thing - <strong>Cloud migration</strong>.</p>
<p>Atlassian still remains a dream organisation for many (thanks to didis and bhaiyas who keep uploading office tour videos). I'll be sharing more interview experiences in next few weeks. Thanks for reading and good luck if you're looking for a change.</p>
<hr />
<p>If you liked reading this, I'd suggest a few more:</p>
<ol>
<li><p>If you're switching from your role, you might want to understand <a target="_blank" href="https://blog.gagan93.me/titles-inflation-and-down-levelling">how levels work</a>.</p>
</li>
<li><p>If you work remotely, then make sure you're doing it <a target="_blank" href="https://blog.gagan93.me/efficient-remote-teams">efficiently</a>.</p>
</li>
<li><p>Multi-tasking can lead to loss of productivity if done in the long run and not done correctly. <a target="_blank" href="https://blog.gagan93.me/multi-tasking-is-not-so-cool">Read</a> to know.</p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Titles, Inflation and Down-levelling]]></title><description><![CDATA[Introduction
If you're confused about career ladders in different organisations, then this post will help you understand semantics of job titles (or job roles, or designations, or levels). There are many stories out there where people switch from a l...]]></description><link>https://blog.gagan93.me/titles-inflation-and-down-levelling</link><guid isPermaLink="true">https://blog.gagan93.me/titles-inflation-and-down-levelling</guid><category><![CDATA[down-levelling]]></category><category><![CDATA[title-inflation]]></category><category><![CDATA[titles]]></category><category><![CDATA[interview]]></category><dc:creator><![CDATA[Gagandeep Singh]]></dc:creator><pubDate>Fri, 19 Apr 2024 07:34:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/KQ0C6WtEGlo/upload/9b8bc4040a23e4fff0100cfd521e2e71.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>If you're confused about career ladders in different organisations, then this post will help you understand semantics of job titles (or job roles, or designations, or levels). There are many stories out there where people switch from a large service-based company to a product company (or one of the FAANG companies) where their titles are different or down-levelled. If you don't have this understanding, you might think - <em>Why would someone move from a current role of Tech lead to a Senior Software Engineer</em>?</p>
<h2 id="heading-understanding-titles">Understanding Titles</h2>
<p>Titles are not standard across companies. And the reason is very obvious - you could open your own company and call yourself a Director/CEO. That might not have any significance unless you have some clients who pay you for the services you provide. As the company grows, you hire more people and/or get more customers. Only then the titles start making some sense. Applying the same example to the scenario where you might join a small company as a Founding engineer/Lead Engineer/VP of Engineering. Here, your initial title doesn't matter because people in startups are <a target="_blank" href="https://emeritus.org/blog/career-generalist-vs-specialist/">generalists</a>: You might be a Software developer by title but at times you could also be setting up servers, doing testing, and even doing customer support. Startups have flat hierarchy where most of the employees are less focussed on titles and more focussed on the mission.</p>
<p>On the other end of this spectrum, we have medium to large organisations where leadership defines specific designations and salary bands. Here, people are very much concerned about their titles because their work and pay aligns with their bands. But that doesn't mean that a Senior Engineer in one big tech does the same work and gets the same pay as the one working in another big tech. Although titles have a range of pay and not everyone on same title gets the same salary (even in a single company) but I hope you get what I'm saying - work and pay differs across companies. This is due to the variations in leadership styles, the complexity of work, and the specific skill sets and experience levels needed to attain a particular job title across different companies.</p>
<p>The closest example in this concern is of my own. I work as a <strong>Staff Engineer</strong> at <a target="_blank" href="https://loconav.com/">LocoNav</a> that has a small engineering team of ~ 100 people. The complexity of my work is not at par with someone who is a <strong>Staff Engineer</strong> at Google, Stripe, or Meta. So if I had to compare myself with a title in such companies, the closest one that aligns is of a <strong>Senior Software Engineer</strong>. But the question is - how did I infer this, and how you can do the same for yourself?</p>
<h3 id="heading-understanding-levels">Understanding levels</h3>
<p>If you want to understand where you currently are w.r.t big tech organisations, simply search something like "Google Software Engineer levels" (or Meta, or Uber). There are many articles already written by ex-employees that describe the responsibilities and expectations of each level. You can match the same with your current responsibilities and understand your level. This is helpful, specially if you're targeting a role at that company.  </p>
<p>I hope the above explains something around titles.</p>
<h2 id="heading-what-is-title-inflation">What is Title Inflation?</h2>
<p>There are some organisations that have a defined career ladder but you get promoted to the next level sooner. As an example, I've seen people in small companies that have title of <strong>Principal Engineer</strong> which is a really senior technical position if we compare the same title to people in large organisations. This is majorly for two reasons:</p>
<ol>
<li><p>Although the median tenure in the industry for software developers is ~ 4 years, there are people who do not change job for a 8-10 years. Organisations create new positions for such people so that they can be promoted (I've seen this myself). In small teams, people with ~ 10 years of experience could be Principal Engineers, while large organisations usually consider people in the range of 12-15 years eligible for this position (although exceptions are always there).</p>
</li>
<li><p>Sometimes, organisations <a target="_blank" href="https://en.wikipedia.org/wiki/Job_title_inflation">inflate your title</a> in order to retain you when you quit. This may or may not include a hike in your pay (based on your negotiation) but moves you to the next level. Not debating if this is right or wrong, fair or unfair but it is what it is :)</p>
</li>
</ol>
<p>The second scenario is a typical case of title inflation. Advancing to the next level can boost your confidence, but it might not benefit you in the long term, especially if your current skills don't align with that level. This situation could even lead to being down-levelled in your next position.</p>
<h2 id="heading-down-levelling-explained">Down-levelling explained</h2>
<p>As the name suggests, it's like getting a demotion (moving to a lower level). We need to understand when it actually concern us:</p>
<ol>
<li><p>If you're in the same organisation and you're getting down-levelled, then it might be a serious concern. You can talk to your manager to understand why this happened. If this is due to some performance concerns, then you might already be expecting this. Incase there is a major restructuring happening in the organisation and all the job roles are being redefined, this might be fine. It all depends on the situation and how you negotiate.</p>
</li>
<li><p>If you're moving to some other organisation, you must learn about their levelling (as already explained above). If you are getting down-levelled while changing the organisation, it might not be wrong because of different role expectations in the next organisation. Although in all cases, you must negotiate with the hiring manager and understand the expectation of next level to see if you can move to the next it :)</p>
</li>
</ol>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I hope this clears up any confusion about different companies and their job titles.<strong>.</strong> It's mostly about the organisation, their expectations, and their team's size that defines the levels. If you're moving to a different organisation and are being considered at a lower level, don't get sad. Understand their job role expectations. At the same time, if you feel that you meet the expectations of a higher level, never settle for less.</p>
<p>Thanks for reading</p>
<hr />
<p>If you liked reading this, I'd suggest a few more:</p>
<ol>
<li><p>Ever wondered what skills are essential for Developers who want to learn about Devops? Read <a target="_blank" href="https://blog.gagan93.me/devops-essentials-for-developers">this</a>.</p>
</li>
<li><p>If you work remotely, then make sure you're doing it <a target="_blank" href="https://blog.gagan93.me/efficient-remote-teams">efficiently</a>.</p>
</li>
<li><p>Multi-tasking can lead to loss of productivity if done in the long run and not done correctly. <a target="_blank" href="https://blog.gagan93.me/multi-tasking-is-not-so-cool">Read</a> to know.</p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Cloud, SaaS Cost Reduction]]></title><description><![CDATA[Background
Reducing spend on SaaS tools is a continuous effort. At LocoNav, we keep a strict eye on such expenses and spend some time every now and then to make sure we're not paying extra for any service. In the past 1.5 years, we planned and execut...]]></description><link>https://blog.gagan93.me/cloud-saas-cost-reduction</link><guid isPermaLink="true">https://blog.gagan93.me/cloud-saas-cost-reduction</guid><category><![CDATA[Cloud]]></category><category><![CDATA[SaaS]]></category><category><![CDATA[cost-optimisation]]></category><dc:creator><![CDATA[Gagandeep Singh]]></dc:creator><pubDate>Thu, 21 Mar 2024 05:23:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/26h317_UMYM/upload/fe0bd0147ec656d8a1078a4e4e75682f.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-background">Background</h2>
<p>Reducing spend on SaaS tools is a continuous effort. At LocoNav, we keep a strict eye on such expenses and spend some time every now and then to make sure we're not paying extra for any service. In the past 1.5 years, we planned and executed a lot of tasks to reduce our spends. This included what we pay to:</p>
<ol>
<li><p>Cloud platforms,</p>
</li>
<li><p>Observability platforms,</p>
</li>
<li><p>Collaboration platforms,</p>
</li>
<li><p>And other SaaS tool (eg. we use location related services heavily in our products as we're a <a target="_blank" href="https://loconav.com/">fleet management product</a>).</p>
</li>
</ol>
<p>This task was taken up by multiple people including myself and we had to figure out our usage of each tool in a way that we can either optimise them or (possibly) replace them. So the thought process was broken into these categories:</p>
<ol>
<li><p><strong>Unused resources</strong> - Are there unused resources lying that can be freed to save some 💰? This includes unused Github seats, a server that was mistakenly left running, a disk volume that's lying unattached after terminating a machine, etc.</p>
</li>
<li><p><strong>Optimising Data volume</strong> - Are there tools where we could send less data without impacting the usage? For example, you can control how much data is sent to APM tools and save some cost here because general trend still remains (almost) same and they bill you for the amount of data you send.</p>
</li>
<li><p><strong>Finding alternative tools</strong> - Are there tools that can be replaced with cheaper or open source (self-hosted) alternatives?</p>
</li>
<li><p><strong>Architectural optimisations</strong> - Can we plan short term and long term architectural changes that would help us save cost? We knew a few things that could be architected better. So we prioritised tasks in a way that have high ROI in the short term were picked first while others were parked for the long term.</p>
</li>
</ol>
<p>The above thought process looks very structured. Frankly, we did not start like this but this is how it eventually turned out to be.</p>
<h2 id="heading-figuring-out-everything">Figuring out everything</h2>
<h3 id="heading-unused-resources">Unused resources</h3>
<ol>
<li><p><strong>SaaS platforms</strong> - Most of the SaaS applications have pricing based on no. of users. While onboarding new team members is a usual activity and happens sooner or later, but it's equally important to offboard them once they leave. In small teams, this leak often remains undetected for months. Beyond cost, this could have other risks unless the user logs in through SSO. So make sure to deactivate accounts and free those seats when users leave. It might have a small impact on monthly bills but it is important to have a complete list of apps where you need to deactivate user accounts.</p>
</li>
<li><p><strong>Cloud platforms</strong> - It's hard to accurately understand and justify cloud spends but members of the team should still have an idea on how many databases, virtual machines and other cloud resources they're using. If you have a single cloud account that multiple teams use, I'd recommend you to <a target="_blank" href="https://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/cost-alloc-tags.html">tag the resources</a> used by respective teams. Once done, it becomes easy to discuss and optimise the cost for each team. Beyond this, you could figure out things that are not used or could be combined. For example, we figured out and did all this:</p>
<ol>
<li><p><strong>Buckets</strong> - Check unused S3 buckets and deleted them. Modified storage class for a few of them and set intelligent tiering to a few.</p>
</li>
<li><p><strong>EC2/EBS</strong> - We moved to EKS for many apps so we deleted <a target="_blank" href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html">AMIs</a> for those apps, that helped us free up a lot of space used by snapshots. Apart from this - basic optimisation like deleting unused machines that were left mistakenly, checking instance usage and resizing machines were also done.</p>
</li>
<li><p><strong>Merging few deployments</strong> - There were a lot of small projects (both internal and customer facing) which used individual databases, caches and machines. We consolidated databases for a lot of internal tools on a single RDS that still didn't peak to even 50% of CPU after combining. Thankfully all our relational use cases are on Postgres that helped in this consolidation (read about <a target="_blank" href="https://blog.gagan93.me/avoid-redundant-complexity">embracing simplicity</a>). This was planned for some other projects also that were customer facing but very much related to each other and could be combined.<br /> Note: Don't overdo this as this has one potential downside - incase you upgrade this RDS in future in a way that it needs a downtime, you'd need to take downtime on all the apps that use this RDS.</p>
</li>
</ol>
</li>
</ol>
<h3 id="heading-optimising-data-volume">Optimising data volume</h3>
<p>Before understanding what we optimised, I'll share some background on data transfer (ingress and outgress) costs. Data transfer is one of the most trickiest component in a cloud provider's bill. For many managed resources, you pay for the amount of data transferred through them (apart from the basic cost of running them). <a target="_blank" href="https://aws.amazon.com/elasticloadbalancing/pricing/">AWS load balancer</a> is a perfect example of this where you pay a monthly cost of using it and another cost for the amount of data transferred. Even beyond this, if the data goes out to the public internet (outside AWS) or a <a target="_blank" href="https://aws.amazon.com/about-aws/global-infrastructure/regions_az/">different region</a>, you get charged extra. The data transfer cost is negligible for personal projects / small apps but it becomes a considerable part of your bill if your apps have a lot of data flowing outside the AZ/Region. The point is - how do I know if the data goes out of cloud provider 🤔? The answer is not so simple and it varies from <a target="_blank" href="https://babatrucks.atlassian.net/browse/INTAKE-7424">one provider</a> to <a target="_blank" href="https://azure.microsoft.com/en-in/pricing/details/bandwidth/">another</a>. As a thumb rule, assume you'll be charged for data if:</p>
<ol>
<li><p>It goes out of your AZ (availability zone) or region.</p>
</li>
<li><p>You connect to a service that runs on another cloud provider (or generally on public internet).</p>
</li>
</ol>
<p>Coming back to optimising data volume - We use Datadog for Application Performance Monitoring (APM). Some of our large applications send a lot of data to Datadog. If you check their <a target="_blank" href="https://docs.datadoghq.com/account_management/billing/pricing/#apm">APM billing</a>, you'll realise that they consider <em>amount of spans ingested</em> for charging the user. Because the data can be very large for mid-large applications, they give an <a target="_blank" href="https://docs.datadoghq.com/tracing/guide/ingestion_sampling_use_cases/#reducing-ingestion-for-high-volume-services">option to control</a> how much percentage of data we want to sample. This reduces costs in two ways: data received at the APM layer is lower (hence less bill) and data emitted from our system is also less (hence less transfer charges). Here, the data transfer cost would impact us only if Datadog's infrastructure is deployed in a different AZ/Region/Cloud provider. As of March 2023, when someone <a target="_blank" href="https://www.linkedin.com/pulse/datadog-outage-multi-cloud-reliability-dylan-ratcliffe/">unofficially investigated</a> Datadog's outage, he found out that Datadog uses multi-cloud deployment strategy, i.e. they deploy in AWS, Azure and GCP. So you can believe that your data is most probably going out of the cloud provider.</p>
<p>For your apps - If you start with this basic knowledge of 'how your cloud provider charges for data transfer', you can optimise a few things on the bill 😀.</p>
<h3 id="heading-finding-alternative-tools">Finding Alternative tools</h3>
<p>This was a medium-hard problem because shifting people to a new tool is hard. Being a team of ~ 100 software engineers, we use a lot of industry standard tools like APM tools, error trackers, server and website monitoring tools, communication tools and what not. We moved from NewRelic to Datadog for APM back in 2021 (due to cost reasons) and I remember the switch was not easy. While both are very good tools and best in industry, we were habitual of using NewRelic for debugging our slowness and outage cases. It took some time to adjust to Datadog's features and UX and now we find it quite useful to debug such cases (specially the <a target="_blank" href="https://docs.datadoghq.com/monitors/types/watchdog/?tab=apm">Watchdog</a> feature 👌). Still we tried to find alternate platforms for whatever was possible to replace and started using <a target="_blank" href="https://www.getoutline.com/">this tool</a> for documentations.</p>
<p><strong>Can you also do this ? -</strong> It depends on how much your team is flexible in moving to the new tool. It also depends on the pricing model of your current tool. Like if you've already committed or paid for next few months or an year, then you could defer the plan to move. But always try to use tools that allow exporting data into common (non-proprietary) formats. For example, although JIRA is a industry standard tool for managing team sprints, there is <a target="_blank" href="https://community.atlassian.com/t5/Jira-Software-questions/How-to-export-Jira-issues-to-CSV/qaq-p/175115">no way to export all the issues</a> as CSV. The best answer on this community post allows to export max 1000 issues at once. Although importing data to JIRA is easy and explained <a target="_blank" href="https://support.atlassian.com/jira-cloud-administration/docs/migrate-from-other-issue-trackers/">here</a> 🤓.</p>
<h3 id="heading-architectural-optimisations">Architectural optimisations</h3>
<p>These optimisations majorly depend on your own project. For medium-large projects that are live in production from years, the teams are already aware of tech debt and architectural debt. Beyond debt, there could be technological advancements that you can leverage. As an example, Redis is a popular in-memory key-value database. The initial version of Redis was released almost 15 years ago. While there have been a lot of changes and features additions even recently, the basic architecture of command processing remains the same - a single threaded command execution thread. Don't underestimate the speed of Redis after reading <em>single threaded.</em> While Redis works well for applications of all sizes, there have been a lot of choices now, as Paypal open sourced it's <a target="_blank" href="https://github.com/paypal/junodb">key value store</a>, a team of developers released a new key value database called <a target="_blank" href="https://www.dragonflydb.io/">Dragonfly</a> and Microsoft introduced <a target="_blank" href="https://www.microsoft.com/en-us/research/blog/introducing-garnet-an-open-source-next-generation-faster-cache-store-for-accelerating-applications-and-services/">Garnet</a> this week. Not everyone wants to move their existing systems to new (promising) databases and you should actually never do that, but I was surprised to see a very stable and <a target="_blank" href="https://github.com/sidekiq/sidekiq/commit/250cc1e4dde44e84d9b607a2ded84ec0835a3ef1">popular Ruby library</a> offering choice between Redis and DragonflyDB as a queueing backend for background jobs 😃.</p>
<p>Developers rewrite systems all the time, but that choice is not always available. Even when allowed, large rewrites often fail to replace existing (stable) systems due to one or the other reason. To optimise our costs, we tried to rewrite parts of the systems that we were sure of. Some infrastructure changes also helped us save cost but that impacted our architecture and also how our team debugs production issues. We tried and did the following:</p>
<ol>
<li><p>We moved a lot of our applications to Kubernetes cluster. The apps were tested for more than an year on non production environments before moving. Using a combination of AWS on-demand and spot instances clubbed with Kubernetes' <a target="_blank" href="https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/">HPA</a>, we were able to save on cloud costs. Spot instances helped us using same infra on a cheaper price, while some on-demand nodes balanced out for stability. HPA allowed us to turn off pods when load on the system was less (eg. at night).</p>
</li>
<li><p>We planned, tested and moved our ruby application server from a single threaded (<a target="_blank" href="https://github.com/phusion/passenger">passenger</a>) to multi-threaded (<a target="_blank" href="https://github.com/puma/puma">puma</a>). We had to validate thread safety of our code and third party libraries before doing this.</p>
</li>
<li><p>We refactored our <a target="_blank" href="https://github.com/sidekiq/sidekiq">background job server</a> setup to lesser number of queues that simplified our deployment one EC2 and eventually on k8s. This also helped in saving cost as pods were getting utilised to their maximum extent and HPA scaling pods up and down as needed.</p>
</li>
<li><p>Other optimisations included rewriting a few systems, details of which cannot be shared (and also won't be helpful to anyone). These were mostly long term optimisations.</p>
</li>
</ol>
<h1 id="heading-conclusion">Conclusion</h1>
<p>This was by no means a single person's work. A lot of senior engineers and devops collaborated to plan and execute all these activities but the gains were rewarding and for the long term. These are recurring bills, so saving even $500 a month means saving $6000 annually. After seeing all these optimisations, I often think - <em>were we really wasting this much money per month</em> 🤔? The answer for this might depend on the team/company. In a growing team, such scenarios often happen. New teams, new projects, new deployments can lead to some inefficiencies over time. Regular consolidations and some approval flow from centralised teams can help in reducing this. For teams that have dedicated people for optimising costs and ensuring efficient use of resources, these instances should be minimum. If you're a team that has not yet focussed on optimising such costs, do try a few things mentioned here.</p>
<h1 id="heading-bonus">Bonus</h1>
<ol>
<li><p><strong>Budget alerts</strong> - Many platforms allow setting alerts on user defined thresholds (<a target="_blank" href="https://docs.github.com/en/billing/managing-billing-for-github-actions/managing-your-spending-limit-for-github-actions">example 1</a>, <a target="_blank" href="https://aws.amazon.com/aws-cost-management/aws-budgets/">example 2</a>). You can set these up to catch anomalies in costs. We have some of these setup that often help us.</p>
</li>
<li><p><strong>Dedicated ownership -</strong> Software teams rely on many tools. As the team grows, it's hard for one person to keep a track of everything. At the same time, keeping a basic check on costs and anomalies doesn't require a lot of expertise. If you assign some people to dedicatedly monitor some tools, it will not burden anyone and will ensure catching such leaks early.</p>
</li>
<li><p><strong>Continuous monitoring</strong> - In the past, we use to ignore a lot of platforms and were optimising only cloud bills. This was our first mistake as other bills were not negligible. Second mistake was to check for optimisations only a month before our reservations expired. Cost optimisation is not an annual activity. We should be continuously monitoring these costs.</p>
</li>
</ol>
<hr />
<p>Thanks for reading. I hope this would help you to optimise some SaaS cost for your organisation. If you liked this post, do check out the following:</p>
<ol>
<li><p>Make sure your stack is <a target="_blank" href="https://blog.gagan93.me/cloud-pricing-vendor-lock-ins">not vendor locked-in</a> if you want flexibility of moving out in future (or maybe otherwise also)</p>
</li>
<li><p>A detailed post on <a target="_blank" href="https://blog.gagan93.me/optimising-docker-builds">optimising docker build time and build size</a>.</p>
</li>
<li><p>Working in a team requires asking for help every now and then. Learn <a target="_blank" href="https://blog.gagan93.me/asking-good-questions">how to ask good questions</a>.</p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Developing From Scratch: 2016 vs. now]]></title><description><![CDATA[Background
One year into the industry, I wasn't sure if I knew enough about full-stack software development. I was working on a maintenance project, where we were mostly scaling down the system, doing small bug-fixes, doing a lot of debugging and wri...]]></description><link>https://blog.gagan93.me/developing-from-scratch-2016-vs-now</link><guid isPermaLink="true">https://blog.gagan93.me/developing-from-scratch-2016-vs-now</guid><category><![CDATA[development]]></category><category><![CDATA[scratch]]></category><category><![CDATA[buildingandlearning]]></category><dc:creator><![CDATA[Gagandeep Singh]]></dc:creator><pubDate>Fri, 15 Mar 2024 12:13:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/XasZ01m128I/upload/5cdb3d3dca0c378ff90bf588094494c7.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-background">Background</h2>
<p>One year into the industry, I wasn't sure if I knew enough about full-stack software development. I was working on a maintenance project, where we were mostly scaling down the system, doing small bug-fixes, doing a lot of debugging and writing a little bit of new code. I visited my college in 2016 where I met some juniors and the placement head. I discussed the idea of creating an Alumni website for our college because no such connection existed between the college and it's alumni. While I was into full-stack development but I didn't like doing frontend a lot. So I discussed the idea with my friend <a target="_blank" href="https://www.linkedin.com/in/ekas/">Ekas</a> (a year junior to me and just out of college) and we both started working on it. This was completely out of fun, not for profit project. This was our tech stack at that time:</p>
<ol>
<li><p><strong>Backend</strong> - Ruby on Rails,</p>
</li>
<li><p><strong>Frontend</strong> - JQuery/Bootstrap,</p>
</li>
<li><p><strong>Database</strong> - MySQL (all user data), Redis (for some caching, probably redundant),</p>
</li>
<li><p><strong>Cloud provider</strong> - Linode (Reason: Non fancy platform &amp; straight forward billing),</p>
</li>
<li><p><strong>Issue tracker</strong> - Trello,</p>
</li>
<li><p><strong>Version control</strong> - Gitlab initially (now Github, <a target="_blank" href="https://github.com/gagan93jtg/gtbit_alumni/">source code</a>),</p>
</li>
<li><p><strong>Communication</strong> - Gmail/Whatsapp,</p>
</li>
<li><p><strong>Domain</strong> - Godaddy,</p>
</li>
<li><p><strong>CDN</strong> - Cloudflare (Wasn't needed, but I needed a free SSL Cert that they provided).</p>
</li>
</ol>
<p>Like many other side projects, the stack was pretty straight forward - We chose what the best we knew. For example, if I knew about <a target="_blank" href="https://letsencrypt.org/">LetsEncrypt</a> for SSL certificate, I'd not have used Cloudflare because CDN was not required at that time.</p>
<h2 id="heading-developing-something-in-2024">Developing something in 2024</h2>
<p>In past 8 years, software landscape has seen major changes. And with recent advancements in AI, we're able to write code faster. Many low-code and no-code tools are also available if you need to build simple things (even beyond static websites). For example, I had to revamp my portfolio website from <a target="_blank" href="https://gagan93.me/v2/">this</a> to <a target="_blank" href="https://gagan93.me/">current</a> two months ago. It took me just a few hours to create the current version because the skeleton and design was <a target="_blank" href="https://www.linkedin.com/posts/gagan93_some-good-prompts-on-chatgpt-helped-me-revamp-activity-7145825841346674688-4ssY">generated by ChatGPT</a>. As recent as this week, <a target="_blank" href="https://www.youtube.com/watch?v=AgyJv2Qelwk">Devin is released</a>, who is world's first AI Software Engineer (although a bold claim to make).</p>
<p>Given all the improvements over time, let's discuss the choices we have if you are (or even I am) developing an MVP or side project today:</p>
<ol>
<li><p><strong>Backend</strong> - Although my major backend experience has been in Rails and it will be a biased opinion if I say Rails is faster to develop than any other framework. But I've seen many people and organisations use Rails to quickly build MVPs. So if I was building something that really needs a complete dynamic and full-stack website of it's own (with UI), I'd still prefer Rails. If there's a simpler requirement where I just need to develop APIs on the backend, and frontend is a mobile platform, I might be inclined to use serverless platform to start with. Reason for choosing serverless is simple - the deployment strategies are simpler and I'd pay for the time I'm using the backend, rather than running servers full-time. <a target="_blank" href="https://docs.rubyonjets.com/">Ruby on Jets</a> is one such option if you're a Rails developer. It's just a wrapper over AWS Lambda, Gateway and databases. You can also use Lambda directly rather than using this fancy wrapper, and write your functions in Java, NodeJS, Python, Go and other <a target="_blank" href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html#runtimes-supported">platforms supported by Lambda</a>. Another stack is very popular these days that uses Google Cloud services like Cloud functions as backend, Firestore as a NoSQL database. If you're planning to use AWS, you might want to consider serverless databases like <a target="_blank" href="https://aws.amazon.com/rds/aurora/serverless/">Aurora Serverless</a> (for SQL) and <a target="_blank" href="https://aws.amazon.com/about-aws/whats-new/2023/11/amazon-elasticache-serverless/">Elasticache Serverless</a> (for Redis/Memcached) or the famous <a target="_blank" href="https://aws.amazon.com/dynamodb/">DynamoDB</a>.</p>
</li>
<li><p><strong>Frontend</strong> - In the past 4-5 years, I've worked quite less on the frontend side. My project's frontend is in ReactJS and I've never written code in it. For some internal portals and for my personal portfolio, I still get to work on HTML, CSS (sass), Bootstrap and Jquery. If I get someone to handle the frontend part, I might want to use React/Next/Vue on the frontend side with a modern CSS framework like Tailwind. But if I'm handling the frontend myself, I'd still use the old stack. It's not that I don't want to learn something new, but because I'd prioritise delivery speed over anything for the MVP. This is the single most important thing to consider when building MVP. <strong>Unless there's a significant speed boost in using a new technology, don't run towards it while validating your idea/product.</strong> AFAIK, if I use a modern framework and write code in Typescript, I'd need a transpiler like webpack to convert it into JS code before deploying. And I'd not want to <a target="_blank" href="https://blog.gagan93.me/avoid-redundant-complexity">add more complexity</a> in the beginning unless it's worth.</p>
</li>
<li><p><strong>Deployment / Hosting -</strong> If you plan to use cloud functions / lambdas for your backend, you might not need any place to host your server. Your code could be deployed easily by the UI provided by the platform. Incase you're still need to run your app server on a virtual machine, there are a few options like</p>
<ol>
<li><p>Deploying app directly on VM.</p>
</li>
<li><p>Deploying app as a container on VM (behind Nginx proxy maybe)</p>
</li>
<li><p>Deploying app as a container on managed platform (like AWS ECS)</p>
</li>
<li><p>Deploying app as a container on orchestration platform (like AWS EKS)</p>
</li>
</ol>
</li>
</ol>
<p>    If you don't have experience with Docker or Kubernetes, just use the first option. it's like deploying app on the server in the same way you'd setup on your local machine (with minimal differences). If you have experience with Docker and Kubernetes, still prefer to choose a service that's cheap to start with. For example, you don't need a full-blown EKS cluster to start with as it would cost $70-75 per month just to have the cluster running (while you could get a t4g.xlarge [4CPU / 16GB] machine in that cost). Given my expertise with containers and ease of deployment, I'd prefer 2nd or 3rd option.</p>
<ol start="4">
<li><p><strong>Database</strong> - I see a lot of new folks preferring NoSQL databases for all kinds of use cases. All new shiny tech looks good unless you fall face operational complexities. That being said, I'd prefer SQL database if following conditions are satisfied:</p>
<ol>
<li><p>The requirements enforce some relation between the entities (that can be easily modelled as foreign keys).</p>
</li>
<li><p>The data is not just append only (i.e. it probably needs updation).</p>
</li>
<li><p>There are (probably) transactional requirements.</p>
</li>
</ol>
</li>
</ol>
<p>    Even if 2/3 are true, it makes sense to use an SQL database. If not, you can go for a NoSQL database. There are many choices in both of these, which are already discussed in the first point.</p>
<ol start="5">
<li><p><strong>Cloud Provider</strong> - If you're having experience with any cloud provider and understand their billing well, choose them blindly (just kidding). Cloud billing is the most complex thing ever invented by these guys. I've seen people struggling on their personal account (with their own credit card) when they're not able to understand what they're getting billed for. Even if you sit with your project's Devops team and ask them to explain the components of this month's bill, I bet there will be parts they can't explain completely. Discussing cloud bills is a complete topic on it's own and the subject is so complex that there are dedicated companies that help you lower your cloud bill (eg. <a target="_blank" href="https://www.duckbillgroup.com/">1</a>, <a target="_blank" href="https://www.builder.ai/builder-cloud">2</a>). So if you have experience using AWS and you either understand their billing, or at least know how you can terminate everything and remove your credit card, then choose that provider. For your use case (i.e. an MVP), there won't be huge difference in cloud costs. If you don't have experience with cloud providers, do one of the following:</p>
<ol>
<li><p>Choose any one provider, learn how to setup budget alerts and remove your credit card if something goes out of hands.</p>
</li>
<li><p>Find a dead simple one - I chose Linode because I had used it in my project and they gave only one service (plain unix VM). There was no additional service provided by them (like machine images, container hosting, security groups, load balancers) and hence billing was very simple. The server I ran back in 2016 costed me $10 per month (nothing more, nothing less).</p>
</li>
</ol>
</li>
<li><p><strong>Issue tracker</strong> - Use anything that's free. You could even use google sheets to keep things simple.</p>
</li>
<li><p><strong>Communication</strong> - Whatsapp has grown into a very nice communication tool but nothing beats a tool like Slack 😍. I just checked that they provide a free plan with limited features and 3 month message history. If that works for you, spin a new slack instance.</p>
</li>
<li><p><strong>Version control</strong> - Github is a popular choice here. I'd happily use it because they now allow private repos for free. So incase you're building something that could grow into a paid product later, you'd want to keep your code private. I've used Gitlab and bitbucket also. Almost all of them support CI/CD setup incase you're looking to have that for your project.</p>
</li>
<li><p><strong>Domain</strong> - You need a domain for your project unless you're happy hosting as a subdomain of <code>.herokuapp</code> or <code>.netlify</code> . I have a few domains and I find Godaddy cheap for domains. You can compare providers and see whatever works for you. There's nothing special with domain providers. There are ways to migrate from one provider to another (it takes 1-2 days) if you need to do that in future. Also, you can move DNS management (that routing thing) to some other provider if you want. So in my organisation, we have DNS on AWS Route 53 while domain management is with Godaddy. That's doable if you need to do that in future.</p>
</li>
<li><p><strong>CDN</strong> - I think to start with, I won't need it unless I'm creating paid videos for my DSA/System Design course 🤣 (jokes apart: because videos are static resources that could be served fast using a CDN). If we need a CDN, we can choose between popular ones like Akamai, Cloudflare or AWS Cloudfront. I don't have any strong opinions here.</p>
</li>
</ol>
<p>More things? Of course, we did not talk about Load balancers, API Gateways, Firewalls and many other components. In most of the cases, you won't need them when starting up. The idea was to outline the choices available today vs those available back then. There were lesser cloud providers with fewer services, Docker was fairly new, managed docker services were limited, and Linode was dead simple (and not owned by Akamai). While choosing your stack, just make sure you're not too much locked into one provider. For example, while Google's stack sounds simple and cheap to begin with, you'll be locked with it completely if you build on it. It's not impossible to migrate but it could be hard or expensive. Another example could be of AWS RDS - If you're using a Postgres/MySQL database hosted on RDS, that doesn't completely lock you with AWS as Postgres could be self hosted outside AWS also. But if you use something like DynamoDB, then you can't move outside AWS unless you find an alternative, change your code and move all your data to the new datastore. If you write your code in a modular fashion and follow good design patterns (like a <a target="_blank" href="https://refactoring.guru/design-patterns/facade">facade</a> over the classes that interact with DynamoDB), you could easily switch to new database. But if your code is coupled deeply, it could take time to refactor everything and <a target="_blank" href="https://blog.gagan93.me/cloud-pricing-vendor-lock-ins">take yourself out from AWS</a>.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>As software developers, we often face <a target="_blank" href="https://en.wikipedia.org/wiki/Impostor_syndrome">imposter syndrome</a> and feel like we don't know nothing beyond our company projects. Building something in parallel could be a way to sharpen your skills and increase confidence. If you have time, you can build something in a new tech stack. If not, feel free to choose your existing stack and build it. Sadly, I got too busy with my work that I did not spare enough time to build anything else like the Alumni website. But thankfully I started writing last year and I enjoy that (I hope you too enjoy reading the blogs 😊).</p>
<hr />
<p>Want to read more such posts? I'll recommend these:</p>
<ol>
<li><p><a target="_blank" href="https://blog.gagan93.me/avoid-redundant-complexity">Avoiding design complexities</a> when building something simple.</p>
</li>
<li><p>Read about <a target="_blank" href="https://blog.gagan93.me/cloud-pricing-vendor-lock-ins">vendor lock-ins</a> in detail.</p>
</li>
<li><p>If you work in a fullstack team that struggles to deliver efficiently, then <a target="_blank" href="https://blog.gagan93.me/efficient-fullstack-delivery">this is for you</a>.</p>
</li>
</ol>
]]></content:encoded></item></channel></rss>