<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Akshat Shah</title>
    <description>The latest articles on DEV Community by Akshat Shah (@akshatshah21).</description>
    <link>https://dev.to/akshatshah21</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F358724%2F8a4d7bd2-876e-426e-9228-332f8ec28e63.png</url>
      <title>DEV Community: Akshat Shah</title>
      <link>https://dev.to/akshatshah21</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/akshatshah21"/>
    <language>en</language>
    <item>
      <title>Circuit Breaker - Hope is not a Design Method</title>
      <dc:creator>Akshat Shah</dc:creator>
      <pubDate>Sun, 18 Jun 2023 11:45:10 +0000</pubDate>
      <link>https://dev.to/akshatshah21/circuit-breaker-hope-is-not-a-design-method-1bni</link>
      <guid>https://dev.to/akshatshah21/circuit-breaker-hope-is-not-a-design-method-1bni</guid>
      <description>&lt;p&gt;TLDR: The Circuit Breaker pattern - fail fast when it's likely that an operation will fail based on its previous invocations.&lt;/p&gt;

&lt;h1&gt;
  
  
  Context
&lt;/h1&gt;

&lt;p&gt;Say you're placing an order on a groceries app. You make the payment and you're waiting for that happy payment confirmation screen with its melodious chime that almost always appears immediately. However, this time, it does not. You wait thirty long, painful seconds and are eventually annoyed to see a &lt;em&gt;"Could not confirm payment"&lt;/em&gt; message.&lt;/p&gt;

&lt;p&gt;Now let's say this is what normally happens when you make a payment: the app on your phone polls an order service hosted somewhere remote, which in turn polls a dedicated payment confirmation service. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iLDJrNlo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sbiei57qa869hr6bmjte.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iLDJrNlo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sbiei57qa869hr6bmjte.png" alt="The example situation - a groceries app, with an order service and a payment confirmation service" width="800" height="198"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's say that the payment confirmation service is down due to some reason.&lt;/p&gt;

&lt;p&gt;In a distributed system such as this, where there are multiple remote service calls to serve a transaction, failures can be broadly categorized into two types, based on their duration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Transient failures&lt;/strong&gt;: these are resolved automatically after a short time interval (seconds). Reasons for these may include timeouts due to slow network connections, high load on the server, or any temporary issue. These can be handled by &lt;em&gt;retrying&lt;/em&gt; the call.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failures that will take longer to fix&lt;/strong&gt;: for example, the complete failure of a service or broken network connectivity. These are the type of failures that we'll look at.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  What Happens When a Dependency Fails
&lt;/h1&gt;

&lt;p&gt;Let's say we handle all failures of this &lt;em&gt;frequently executed operation&lt;/em&gt; with a retry mechanism. The calling application (order service in the example above) will continually retry the remote call (a specified number of times). But we know that the retries will not succeed since the failure at the dependency (payment confirmation service in the above example) is not transient. What are the consequences?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The transaction is blocked&lt;/strong&gt; on this operation. It is using up resources - memory, threads, database connections and CPU cycles that are involved with this transaction. This is especially impactful to the overall performance of the calling application if the number of transactions that involve the dependency is high in volume or frequency - like grocery orders!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blocked transactions and holding resources wastefully will cause failures to cascade&lt;/strong&gt; further to possibly unrelated parts of the system that might need to use the same resources. And this is not just at the calling service level where memory, threads, DB connections et cetera are being held. The service itself could be a resource to other remote components, which might also get affected since the calling service is not behaving as usual. In the grocery order payment example, resources on the user's phone are being used by the app polling the order service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Continuous retries to the already failing dependency&lt;/strong&gt; might put further load on it and perhaps prevent it from recovering&lt;/li&gt;
&lt;li&gt;And, in the example above, you have a lot of annoyed users that waited a long thirty seconds!&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Great, Now What?
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Hope is not a design method."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;~ Michael T. Nygard, &lt;a href="https://pragprog.com/titles/mnee2/"&gt;Release It!: Design and Deploy Production-Ready Software&lt;/a&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The key point to note here is that non-transient failures of a dependency shouldn't be handled the same way as transient failures. We need a way of &lt;em&gt;detecting that a failure is non-transient and acting accordingly&lt;/em&gt;. Another thing to note is that there's nothing special about a remote service call here; any operation that is going to fail for a while can benefit from such a preventive arrangement. Let's think through what we could do in such a scenario and lay down some requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Detect that the failing operation is probably going to keep failing for a while&lt;/em&gt; - it's not a transient failure&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Fail fast&lt;/em&gt; - don't waste resources attempting the operation; skip that operation and respond immediately with an appropriate exception when it's known that it's likely to fail for a while&lt;/li&gt;
&lt;li&gt;We need to eventually switch back to calling the operation. So, we &lt;em&gt;need to detect if the problem has been fixed, and switch back&lt;/em&gt; when we are confident enough&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Do we really need something like this? Why not just use fewer retries or a shorter timeout if we are worried about wasting resources and burdening services?&lt;/em&gt;&lt;br&gt;
Valid question! The problem with setting shorter timeouts is false negatives. If we reduce the timeout duration, the operation might fail most of the time even though it could succeed given some more time. And for a high-volume transaction, wasted resources and their second-order consequences due to even a few retries can be significant. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Enter the Circuit Breaker
&lt;/h1&gt;

&lt;p&gt;The circuit breaker pattern, popularized by Michael T. Nygard in his book, "&lt;a href="https://pragprog.com/titles/mnee2/"&gt;Release It!&lt;/a&gt;", is what we can use in such a situation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it Does
&lt;/h2&gt;

&lt;p&gt;Simply said, a circuit breaker acts as a &lt;em&gt;proxy&lt;/em&gt; for the operation that might fail. This proxy &lt;em&gt;monitors&lt;/em&gt; the number of recent failures and decides whether to attempt the operation next time it is triggered or return an exception directly, skipping the operation. It tries the operation after some time and decides whether to switch back to invoking the operation normally that it is acting as a proxy for.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;How the failing operation recovers is handled externally, possibly by restoring or restarting a failed component or repairing a network connection.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  States of the Circuit Breaker
&lt;/h2&gt;

&lt;p&gt;Overall, we can guess that the circuit breaker has three states. And the naming of these states is where the &lt;a href="https://en.wikipedia.org/wiki/Circuit_breaker"&gt;"circuit" analogy&lt;/a&gt; comes into play:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rrEorIkV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kwqxazqd9by0uidk73oy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rrEorIkV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kwqxazqd9by0uidk73oy.png" alt="The Three States of the Circuit Breaker Pattern" width="800" height="265"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Closed&lt;/strong&gt;: The circuit is &lt;em&gt;closed&lt;/em&gt;, so the request to the circuit breaker is routed to the operation which returns a response - the &lt;em&gt;normal flow&lt;/em&gt;, so to speak. The operation can fail here, and the circuit breaker tracks these failures&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open&lt;/strong&gt;: The circuit is &lt;em&gt;open&lt;/em&gt;, which means the request to the circuit breaker will &lt;em&gt;not&lt;/em&gt; be routed to the operation. Instead, the circuit breaker will return an exception&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Half-open&lt;/strong&gt;: This is the state in which the circuit breaker is 'testing the water'; it allows a limited number of requests it receives to invoke the operation, to check whether the problem with the operation has been fixed&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  State transitions
&lt;/h3&gt;

&lt;p&gt;Let's look at how the circuit breaker goes from one state to another:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xCc1tlv---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/68pat2i7epl27v4l9alb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xCc1tlv---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/68pat2i7epl27v4l9alb.png" alt="State Transitions of a Circuit Breaker" width="800" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Closed -&amp;gt; Open&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;While in the closed state, the circuit breaker maintains a count of the recent failures in invoking the operation&lt;/li&gt;
&lt;li&gt;If this count (with recency kept in consideration) exceeds the &lt;code&gt;failureThreshold&lt;/code&gt;, the circuit breaker transitions to the open state&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open -&amp;gt; Half-Open&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;While in the open state, a timer is set for &lt;code&gt;breakDuration&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;During this time, the operation is not invoked at all, and the circuit breaker responds with an exception. This is also the time during which the fault is expected to be fixed&lt;/li&gt;
&lt;li&gt;When the timer expires, the circuit breaker switches to the half-open state&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Half-Open -&amp;gt; Open&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Implementations can differ here, but here's one of the simpler ones: the next requests to the circuit breaker when it's in the half-open state &lt;em&gt;are routed to the operation&lt;/em&gt;. If these invocations succeed, depending on the &lt;code&gt;successThreshold&lt;/code&gt;, the circuit breaker will eventually go to the closed state&lt;/li&gt;
&lt;li&gt;However, if there is any failure before &lt;code&gt;successThreshold&lt;/code&gt; is reached, the circuit breaker reverts to the open state&lt;/li&gt;
&lt;li&gt;Another implementation might allow a fraction of the incoming requests to invoke the operation to decide whether to switch to the closed state. This can be helpful, for example, if a remote service being called is recovering and we wouldn't want to flood it immediately with the regular volume of requests.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;In code, the circuit breaker acts as a wrapper around the operation under consideration. So, any calls to the said operation go through the circuit breaker proxy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IkF-OJrl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lhsnx01r6jsz1bo9ps39.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IkF-OJrl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lhsnx01r6jsz1bo9ps39.png" alt="The Circuit Breaker as a Proxy for the Operation" width="800" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Parameters
&lt;/h3&gt;

&lt;p&gt;Different implementations require different parameters to be configured, but here are the core ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;failureThreshold&lt;/code&gt;: The threshold (count or percentage) for failures after which the circuit breaks. Some advanced implementations also couple this with a &lt;code&gt;minimumThroughput&lt;/code&gt;, so the failure threshold only takes effect when there's a minimum amount of traffic&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;breakDuration&lt;/code&gt;: This specifies how long the circuit remains open before switching to the half-open state. Too low and there will be more failed invocations, while too high would mean a lot of failed transactions unless there's a fallback&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;successThreshold&lt;/code&gt;: The threshold (count or percentage) for successful invocations when in the half-open state for switching to the closed state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These values should be tuned by looking at existing traffic patterns and expected recovery time for non-transient failures. It's difficult trying to estimate optimum values without this information.&lt;/p&gt;

&lt;h1&gt;
  
  
  Extras
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Handling the Exception When the Circuit is Open
&lt;/h2&gt;

&lt;p&gt;In the open state, the circuit breaker will return an exception. This exception can be handled according to the application. Here are some options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Report it appropriately&lt;/strong&gt; to the user, asking them to try later. In the grocery app example, perhaps informing the user immediately that there was a problem in confirming their payment might be a little less annoying than making them wait for... thirty long seconds (okay, last time!)&lt;/li&gt;
&lt;li&gt;Try an alternative operati****on, like a fallback. Perhaps using a different way to confirm the payment, or users could be shown alternative payment options on their app&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Temporarily degrade the functionality of the system&lt;/strong&gt; proactively instead of users getting an error at all, like making a payment method unavailable while the circuit is open&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Break the Circuit Based on the Type of Exception
&lt;/h2&gt;

&lt;p&gt;Most circuit breaker implementations allow the specification of the types of exceptions or results that should be considered when breaking a circuit. For example, exceptions arising from valid business logic conditions can be ignored by the circuit breaker while still tracking service unavailability exceptions. In the case of HTTP calls, perhaps some &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses"&gt;&lt;code&gt;4xx&lt;/code&gt;&lt;/a&gt; response codes (client error) could be ignored while &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#server_error_responses"&gt;&lt;code&gt;5xx&lt;/code&gt;&lt;/a&gt; codes could be tracked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Log Circuit Events
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Circuit breakers are a valuable place for monitoring. Any change in breaker state should be logged and breakers should reveal details of their state for deeper monitoring. Breaker behaviour is often a good source of warnings about deeper troubles in the environment.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;~ Martin Fowler, in his &lt;a href="https://martinfowler.com/bliki/CircuitBreaker.html"&gt;blog about circuit breakers&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As an example, the circuit breakers offered by &lt;a href="https://github.com/App-vNext/Polly"&gt;Polly&lt;/a&gt; and &lt;a href="https://resilience4j.readme.io/"&gt;resilience4j&lt;/a&gt; provide hooks into their state transitions, where you could log useful information. These logs could be tied to &lt;em&gt;alerts&lt;/em&gt; for monitoring and observability.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Circuit breaker is a pattern to prevent an application from invoking an operation if it is highly likely to fail. This helps save resources, offer better response times and increase the stability of a system when it is in an error state or recovering from one. However, it's not the best one to use in all cases. Using a circuit breaker isn't recommended for handling access to local private resources in an application like in-memory data structures, since it will add a lot of overhead in this case. As mentioned above, retries are more suitable for transient failures. In fact, circuit breakers can be combined with retries to counter both types. It can also not be used as a substitute for handling business logic exceptions - it's a fault tolerance and resilience pattern.&lt;/p&gt;

&lt;h1&gt;
  
  
  References and Further Reading
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker"&gt;Circuit Breaker pattern - Azure Architecture Center&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://martinfowler.com/bliki/CircuitBreaker.html"&gt;CircuitBreaker - Martin Fowler&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/App-vNext/Polly/wiki/Circuit-Breaker"&gt;App-vNext/Polly - GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern"&gt;Circuit breaker design pattern - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>designpatterns</category>
      <category>softwareengineering</category>
      <category>resilience</category>
      <category>programming</category>
    </item>
    <item>
      <title>Project Pandemonium</title>
      <dc:creator>Akshat Shah</dc:creator>
      <pubDate>Fri, 14 Jan 2022 12:04:29 +0000</pubDate>
      <link>https://dev.to/akshatshah21/project-pandemonium-33fm</link>
      <guid>https://dev.to/akshatshah21/project-pandemonium-33fm</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This blog was originally published by me as part of a &lt;a href="https://medium.com/dscvjti"&gt;blog series&lt;/a&gt; by &lt;a href="https://www.dscvjti.tech/"&gt;Google Student Developer Clubs, VJTI&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A few days ago, during a lazy afternoon, while rummaging through my muddle of a cupboard, I found a notebook that had, unsurprisingly, my name scrawled on it. I opened the book somewhere in the middle, but those pages were blank. Then, opening the first page, I smiled, sheepish and nostalgic. Written on the first twenty or so pages of this notebook, was the account of one of my first projects as an engineering student.&lt;/p&gt;

&lt;p&gt;I immediately took a trip down memory lane. I perused those logs, consisting of long paragraphs of technical details, diagrams and outlines of activities carried out each day. But what stood out were the number of questions and exclamations. Occasionally, a few lines lamented on the roadblocks and failures that were faced, the uncertainty of whether things will work out and whether we were getting anywhere with the project.&lt;/p&gt;

&lt;blockquote&gt;
&lt;h2&gt;
  
  
  About the project
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Along with three friends, I was part of a project that aimed to develop a &lt;a href="https://en.wikipedia.org/wiki/Wi-Fi_positioning_system"&gt;WiFi Positioning System&lt;/a&gt;, or more specifically, an &lt;a href="https://en.wikipedia.org/wiki/Indoor_positioning_system"&gt;Indoor Positioning System&lt;/a&gt;. Fundamentally, this system would determine the location of a device using wireless access points that are in its vicinity. This project was under Eklavya, a mentorship program of the &lt;a href="https://sravjti.in/"&gt;Society of Robotics and Automation, VJTI&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I would have loved to tell you that despite the misgivings in my memoirs, we managed to accomplish our goals. We didn't. And that's what this blog is about.&lt;/p&gt;




&lt;p&gt;A project can be very chaotic. A number of things can go wrong, and every action to correct those mistakes can cascade into a wave of defects. In the end (if there is one), you are left with a good solution, a bad solution, a good solution that is not required, or several other variants. I'm no software engineering expert or a project management expert. I'm a final year undergraduate and I want to share some things I've learned after doing some projects in the last four years. In no way are these things guaranteed to be relevant or useful or even correct. But they're food for thought!&lt;/p&gt;

&lt;h2&gt;
  
  
  But what's the problem?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/l1Et0eHN0m9daH7ZS/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/l1Et0eHN0m9daH7ZS/giphy.gif" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Define the problem. We hear this all the time but seldom do we really put it into effect, or at least with the required level of sincerity, specificity and clarity. &lt;em&gt;"I want to make a chatting application"&lt;/em&gt; might seem self-explanatory as the definition of the required solution, but it's not. The necessity for a clear understanding of the requirements, the scope and priorities, and the assumptions of a solution cannot be emphasized enough. Indeed, this is one of the major reasons why my WiFi Positioning System project was, in subtle terms, a letdown. I vividly remember &lt;em&gt;not&lt;/em&gt; being sure about what we were making. To quote the very first line in my logs:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"After days of ambiguity, our group was able to decide firmly about the project idea - Localization using Triangulation."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Except of course, the ambiguity wasn't gone, and to the keen reader, it must be apparent that the topic mentioned in this excerpt is not what we ended up doing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is that what we had decided?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/DrO4Bm325pjhc0BRM0/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/DrO4Bm325pjhc0BRM0/giphy.gif" width="480" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's important to get most requirements or goals clear and sorted out in the beginning. I say "most" because getting the requirements perfect the first time is extremely difficult, if at all possible. If there are stakeholders of the project, it will indubitably prove difficult to solicit and analyze specific and clear requirements from them at the very beginning of the project - one of the biggest drawbacks of the &lt;a href="https://en.wikipedia.org/wiki/Waterfall_model"&gt;Waterfall model&lt;/a&gt; of software development. Invariably, some form of an iterative process involving prototyping and feedback is part of the trade. But the sooner these requirements get clear, the sooner a project will reach completion, and lesser time will be spent pursuing features or improvements that are not required.&lt;/p&gt;

&lt;p&gt;It's common knowledge that brainstorming in a group can help a lot with this part, because more aspects of the problem and solution will be explored, uncovering more and more questions that will hopefully be answered later on. What's not common knowledge is that all these (likely diverging) lines of thought should be combined to a single, coherent one early enough, otherwise the benefits of collaboration may turn futile.&lt;/p&gt;

&lt;h2&gt;
  
  
  Yes, we can!
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bZkpkdp2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://c.tenor.com/ySRrXlrHo2wAAAAC/thor-really.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bZkpkdp2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://c.tenor.com/ySRrXlrHo2wAAAAC/thor-really.gif" width="498" height="413"&gt;&lt;/a&gt;&lt;br&gt;
Can we though?&lt;/p&gt;

&lt;p&gt;Another important aspect of planning is feasibility. Gauging the feasibility of a solution is, admittedly, difficult. It requires experience, which I definitely didn't have then, and no one can have enough. However, research can help here. If it's a research project or maybe a data science project, a literature review is helpful, and in most cases, expected. Published work like papers, blogs, source code repositories can help one understand the work that others pursuing similar goals have done, as well as an understanding of the technical difficulty, skills and resources required, and therefore, the feasibility of the project. Did we do a literature review for WiFi Positioning? Yes! But it was very, very haphazard and half-hearted. To make a literature review worthwhile, document it while doing it (and not when you're typing out the final report). Not only does this make researching an active task, but it helps down the line. What's better, going through a 20-page research paper &lt;em&gt;again&lt;/em&gt; when you need to, or going through a summary that you have written yourself?&lt;/p&gt;

&lt;h2&gt;
  
  
  Divide and Conquer
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TMUrHm5R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dxqs2v9jfsx5tdure146.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TMUrHm5R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dxqs2v9jfsx5tdure146.png" alt="Image description" width="636" height="353"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Okay, very trite. But it's only logical that you break down the project into large functional pieces, then break those pieces down further, and refine these pieces until they're granular enough for someone to work on. But if you're working in a team, it's equally important to break down development into pieces that can be worked on &lt;em&gt;independently&lt;/em&gt;, in parallel. How often have you worked on something where you had to wait for your friend to push that commit so that you can continue your work? This is not only a waste of time but a sign of an ineffective breakdown of tasks. With experience, this becomes easier and natural to prevent.&lt;/p&gt;

&lt;p&gt;With great division of work, comes the great requirement of reproducibility. Hopefully, you'll use a version control system, like &lt;a href="https://git-scm.com/"&gt;git&lt;/a&gt;. For those who don't know, &lt;a href="https://www.atlassian.com/git/tutorials/what-is-version-control"&gt;version control&lt;/a&gt; is the practice of tracking and managing changes to software code. Also use virtual environments and containers wherever appropriate. The focus of team members should be on the task at hand, not on things such as incompatibilities and installations. Use files for configuration (and check the non-confidential parts into version control) instead of typing out those command-line arguments (or worse, redefining constants in source code; been there, done that) every time. Additionally, combine work &lt;em&gt;nicely&lt;/em&gt;. That means using a standard workflow to introduce changes to the project, like using pull requests, running tests, and other principles of &lt;a href="https://www.atlassian.com/continuous-delivery/continuous-integration"&gt;Continuous Integration&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Write it down, write it down, write it down
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/qlnqDO6nkw8iae2G2s/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/qlnqDO6nkw8iae2G2s/giphy.gif" width="480" height="268"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Again, it's a no-brainer that you should document stuff. Just like you shouldn't stay up too late and get up early. And just like waking up early, very few take this seriously.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;So, one should document stuff? What a drag… Why? We're probably going to write up a final report anyway!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The point of documenting stuff (and humans writing anything in general) is so that you or anyone else can come back to it later if needed. It's communication: between you, your teammates and the same people in the future. It's important to document technicalities and design decisions since they will be revisited. It is important to document tasks, activities and progress so that as a team (or even individually), you know where the project is headed.&lt;/p&gt;

&lt;p&gt;If your project involves running many experiments with different conditions or parameters, consider using a dedicated tool to track them (instead of random TXT files, or notes on your phone, or the worst: messages in your group chat). This can be as simple as using a spreadsheet, but if the experiments are more complex, use specialized tools, such as &lt;a href="https://mlflow.org/"&gt;MLflow&lt;/a&gt; for machine learning models. The point here is to document and facilitate easy review, search and filtering of all the experiments you do.&lt;/p&gt;

&lt;p&gt;A pro tip is to document issues that you have faced, and how you solved them (this is literally how &lt;a href="https://stackoverflow.com/"&gt;Stack Overflow&lt;/a&gt; works; does it sound useful now?). This habit is like an investment. Spend some time now, dig into the problem, and document it. Next time, you're just a Ctrl+F away from solving that problem. This habit also pushes you to actually understand what the problem is and how to solve it, instead of copying something from the documentation or changing parameters randomly (duct tape solutions). To quote my logs:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"In the morning, I was able to solve the GPIO errors. But I still don't know how."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Success: 0 out of 0 tests passed
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/njYrp176NQsHS/giphy-downsized-large.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/njYrp176NQsHS/giphy-downsized-large.gif" width="480" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Iterative and collaborative development &lt;em&gt;needs&lt;/em&gt; automated testing, possibly on different levels: unit, integration and end-to-end. Let's face it, regressions suck. They're a headache and a very annoying side-effect of iteration. Tests can seem laborious, but for a large or long-term project, they're worth it, and provide reliability and confidence in code. And they're likely to be mandatory if you're working for an organization. Also, consider using a code quality checker.&lt;/p&gt;

&lt;p&gt;Along with tests, technical reviews are of enormous benefit. They fuel discussion and brainstorming, and things like bad design or code smells can be detected. Reviews have a great side-effect: knowledge sharing. We hardly did this for our WiFi Positioning project, and it ended up with team members oblivious of how parts developed by others were working. Indeed, grave bugs and opportunities for improvement stay undetected to date.&lt;/p&gt;

&lt;h2&gt;
  
  
  Meetings, meetings everywhere
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--h1-AF1VY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://c.tenor.com/Xgwu6c8wwwMAAAAC/meeting-yesterdays-meeting.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--h1-AF1VY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://c.tenor.com/Xgwu6c8wwwMAAAAC/meeting-yesterdays-meeting.gif" width="476" height="274"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's an example of how a college project group generally gets a meeting scheduled: "Let's meet for project TVA at 10 am". That's usually it, followed by a series of messages negotiating the time of the meeting. This is very likely to be an ineffective, diverging and chaotic meeting. Meetings should be &lt;em&gt;limited&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In terms of &lt;strong&gt;agenda&lt;/strong&gt;. There are different kinds of meetings, for example, stand-up meetings for daily updates or task-specific meetings, e.g., discussion on requirements, or an API design, or design considerations of an ML system. A meeting without a preset agenda will likely be an unfruitful one. Since the agenda is undecided, everyone might not be prepared with all areas of discussion&lt;/li&gt;
&lt;li&gt;In terms of &lt;strong&gt;duration&lt;/strong&gt;: long meetings can reduce their effectiveness, and in some cases also end up wasting time and proxying for productivity rather than adding value&lt;/li&gt;
&lt;li&gt;In terms of &lt;strong&gt;frequency&lt;/strong&gt;; although regularity must be ensured&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Connecting People
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CRlX1M2o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://c.tenor.com/xFgQr7wBL_kAAAAC/how-about-now-ready.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CRlX1M2o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://c.tenor.com/xFgQr7wBL_kAAAAC/how-about-now-ready.gif" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Constant collaboration or being in a state of perpetual connectivity is the trend, using an array of tools like email, Discord, Slack et cetera. But this habit of staying online and open to communication all the time can be counterproductive. One major reason is attention fragmentation. Constant notifications disrupting your work and intermittent switches to replying to a message break concentration on the task at hand, leading to lower quality work than you would be doing in a state of focus.&lt;/p&gt;

&lt;p&gt;According to Cal Newport in his book &lt;em&gt;&lt;a href="https://www.calnewport.com/books/deep-work/"&gt;Deep Work&lt;/a&gt;&lt;/em&gt;, at work, it's common for people to develop the habit of running their day from their inbox: doing tasks as and when you receive emails about it, and using up much of their time replying to emails rather than actually working. This also prevents them from planning activities beforehand and taking a more proactive approach, since it's easier to use the inbox as a to-do list.&lt;/p&gt;

&lt;p&gt;For a project amongst friends, being connected to them constantly for work can easily turn into a hangout. I've lost count of how many times my friends and I have switched from working on a project to playing Skribble on virtual meets.&lt;/p&gt;

&lt;p&gt;All these reasons further justify the constraints that I mentioned that you should keep on meetings. Outside of these meetings, try to hold sessions where you actually work in a focused manner.&lt;/p&gt;




&lt;p&gt;Finally, always remember, software development is hard. Don't beat yourself up if you find things tough, it's part of the journey. And hey, some pages of that book are still blank!&lt;/p&gt;

</description>
      <category>projects</category>
      <category>experience</category>
      <category>thoughts</category>
      <category>productivity</category>
    </item>
    <item>
      <title>A Guide to Install TeX on Linux</title>
      <dc:creator>Akshat Shah</dc:creator>
      <pubDate>Fri, 10 Dec 2021 17:33:28 +0000</pubDate>
      <link>https://dev.to/akshatshah21/a-guide-to-install-tex-on-linux-11jc</link>
      <guid>https://dev.to/akshatshah21/a-guide-to-install-tex-on-linux-11jc</guid>
      <description>&lt;p&gt;This article was originally published on &lt;a href="https://akshatshah21.github.io/tex-install-linux/"&gt;my website&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Story
&lt;/h2&gt;

&lt;p&gt;Recently, I wanted to install a TeX system on my Ubuntu 20.04 PC. I tried installing MiKTeX after someone recommended it to me (thrice...), but I found myself unable to update TeX packages because of a cryptic SSL peer certificate error. I reported this in an &lt;a href="https://github.com/MiKTeX/miktex/issues/935"&gt;issue&lt;/a&gt; to the MiKTeX repo, but was instead told that my system did not support MiKTeX. Great.&lt;/p&gt;

&lt;p&gt;Next, I tried to install TeX Live. It seemed simple enough, with a &lt;code&gt;sudo apt install texlive&lt;/code&gt;. Can't get any easier than this, right?&lt;/p&gt;

&lt;p&gt;So it turns out that the apt package for TeX Live is still the 2019 version. Now unfortunately, this meant that I again could not update packages, since &lt;code&gt;tlmgr&lt;/code&gt;, TeX Live's package manager could not verify the signature for a repository. I tried following &lt;a href="https://tex.stackexchange.com/questions/528634/tlmgr-unexpected-return-value-from-verify-checksum-5"&gt;this post on TeX Stack Exchange&lt;/a&gt;, but this didn't work out.&lt;/p&gt;

&lt;p&gt;So I decided to ditch APT, and tried following the &lt;a href="https://www.tug.org/texlive/quickinstall.html"&gt;TeX Live Quick Install docs&lt;/a&gt; (which is probably I should have done first). But even in this method, the default mirror which was chosen was too slow, and for some reason, the default, &lt;em&gt;recommended&lt;/em&gt; scheme to install TeX Live (called &lt;em&gt;full scheme&lt;/em&gt;) installs around 5GB of TeX software - fonts, packages, et cetera. For someone beginning with TeX, this did not sit well with me.&lt;/p&gt;

&lt;p&gt;Enough with this rant, let me list down the steps that I followed to install TeX Live on my system. Note that there are most likely better methods/choices that one could use, but these steps worked for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is TeX?
&lt;/h2&gt;

&lt;p&gt;Simply said, TeX is a popular typesetting system, especially in academia, that enables one to write documents that involve a lot of formatting, mathematical equations, tables et cetera. It was made by ACM Turing Awardee &lt;a href="https://en.wikipedia.org/wiki/Donald_Knuth"&gt;Donald Knuth&lt;/a&gt; (who is a legend, by the way).&lt;/p&gt;

&lt;h2&gt;
  
  
  TeX Live Installation
&lt;/h2&gt;

&lt;p&gt;TeX Live is a software distribution for TeX, and includes some programs, macros, packages and fonts. It has a package manager specifically for TeX packages, called &lt;code&gt;tlmgr&lt;/code&gt;, that is used to install and update TeX packages.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download &lt;code&gt;install-tl&lt;/code&gt; (the latest one, yay!) using &lt;a href="https://mirror.ctan.org/systems/texlive/tlnet/install-tl-unx.tar.gz"&gt;this link&lt;/a&gt;. In case this gets changed, you can find the link &lt;a href="https://www.tug.org/texlive/acquire-netinstall.html"&gt;in the docs&lt;/a&gt; (Do read the docs once before installing). Extract the contents from the .tar.gz file. Here's how the contents look:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sePuVZv3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j9pw8uoou1h5f33syeum.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sePuVZv3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j9pw8uoou1h5f33syeum.png" alt="Contents of the install-tl directory" width="860" height="98"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Run the &lt;code&gt;install-tl&lt;/code&gt; Perl script as root. Note that the script does not require root permissions, but requires write access to the directory where it's going to install, which by default is &lt;code&gt;/usr/local&lt;/code&gt;. You can change the directory by looking at the prompts of the script. &lt;/p&gt;

&lt;p&gt;Note that you can run the installer in GUI mode by the &lt;code&gt;-gui&lt;/code&gt; flag (which I would recommend if you're doing this for the first time, but it requires Tcl to be installed). &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;--repository&lt;/code&gt; option specifies an explicit mirror to use. Try specifying some other &lt;a href=""&gt;mirrors&lt;/a&gt; if the default mirror seems too slow. In my case, the mirror provided by NUS (and I stay in Mumbai) was much better than the default one.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;perl install-tl &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;-gui&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;--repository&lt;/span&gt; &amp;lt;URL&amp;gt;]
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;I came across the following error when I tried using an explicit mirror:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;install-tl: TLPDB::from_file could not initialize from: https://mirrors.dotsrc.org/ctan/tlpkg/texlive.tlpdb
install-tl: Maybe the repository setting should be changed.
install-tl: More info: https://tug.org/texlive/acquire.html
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Try appending &lt;code&gt;systems/texlive/tlnet&lt;/code&gt; to the mirror URL when running the &lt;code&gt;install-tl&lt;/code&gt; script. For example,&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;perl install-tl &lt;span class="nt"&gt;--repository&lt;/span&gt; https://download.nus.edu.sg/mirror/ctan/systems/texlive/tlnet
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After a few seconds, something similar to the following will be visible:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JoU3b4hk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hqp6ekhwa9wl00hmwo8c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JoU3b4hk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hqp6ekhwa9wl00hmwo8c.png" alt="install-tl script options" width="877" height="832"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you scroll up, you can see the scheme selected by default for installation:&lt;br&gt;
&lt;code&gt;&amp;lt;S&amp;gt; set installation scheme: scheme-full&lt;/code&gt;. Below that, you can see the number of collections and the space they'll take:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; &amp;lt;C&amp;gt; set installation collections:
     40 collections out of 41, disk space required: 7143 MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;7GB by default, not cool.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Enter "S" as the command (upper or lower case), and then you can select the scheme you want. I selected the "medium scheme", which takes around 1.7 GB, obviously at the cost of lesser functionality. But you can install packages as you like later on, using &lt;code&gt;tlmgr&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;After you've selected the scheme you want, enter "R" to return to the main menu, as the prompt will tell you. Then enter "I" to begin installation.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AsW4OHFh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bd418e1nzub15ea1p7yn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AsW4OHFh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bd418e1nzub15ea1p7yn.png" alt="Selecting the installation scheme" width="880" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Depending (a lot!) on the mirror you chose, your installation of TeX Live should finally be complete. For me, it took around 15 minutes. &lt;/p&gt;

&lt;p&gt;Next, you need to update your &lt;code&gt;PATH&lt;/code&gt; environment variable so that it knows where to find TeX Live binaries. If you went along with the default settings, the path you need to add to your &lt;code&gt;PATH&lt;/code&gt; would be something like &lt;code&gt;/usr/local/texlive/2021/bin/x86_64-linux&lt;/code&gt;. Move around inside &lt;code&gt;/usr/local/texlive&lt;/code&gt; to verify this. &lt;/p&gt;

&lt;p&gt;You can update your &lt;code&gt;.profile&lt;/code&gt; (log out and log in again for changes to take place) or &lt;code&gt;.bashrc&lt;/code&gt; or &lt;code&gt;.zshrc&lt;/code&gt; or whatever (source the configuration file for changes to take place) by adding this line:&lt;br&gt;
&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/local/texlive/2021/bin/x86_64-linux:&lt;span class="nv"&gt;$PATH&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Installing a LaTeX editor
&lt;/h2&gt;

&lt;p&gt;I installed TeXstudio by simply (painlessly) using APT.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;texstudio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm happy to say that this worked fine! Most TeX editors should be able to configure the backend (TeX Live, in our case) automatically, provided you have set your &lt;code&gt;PATH&lt;/code&gt; variable and your shell knows about it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6XWQzMO5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/difhdsw1aonw7in4lx0t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6XWQzMO5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/difhdsw1aonw7in4lx0t.png" alt="TeXStudio" width="880" height="550"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://tug.org/texlive/quickinstall.html"&gt;Quick Install - TeX Live&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.tug.org/texlive/acquire-netinstall.html"&gt;Installing TeX Live over the Internet&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://narkive.com/i3g0zxJ0.4"&gt;For the tlpdb error: An answer in a forum discussing "Problems installing TeXlive in CentOS7"&lt;/a&gt;&lt;/p&gt;

</description>
      <category>latex</category>
    </item>
    <item>
      <title>Coding Problem: Two Sum</title>
      <dc:creator>Akshat Shah</dc:creator>
      <pubDate>Fri, 10 Dec 2021 17:20:41 +0000</pubDate>
      <link>https://dev.to/akshatshah21/coding-problem-two-sum-1cnj</link>
      <guid>https://dev.to/akshatshah21/coding-problem-two-sum-1cnj</guid>
      <description>&lt;p&gt;This article was originally posted on &lt;a href="https://akshatshah21.github.io/dcp-1/"&gt;my website&lt;/a&gt;.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.dailycodingproblem.com/"&gt;DailyCodingProblem&lt;/a&gt; is a great website that sends coding problems to your inbox daily.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Question
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Given a list of numbers and a number &lt;code&gt;k&lt;/code&gt;, return whether any two numbers from the list add up to &lt;code&gt;k&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Examples
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;[1, 2, 3], k = 4&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Yes, since 1+3 = 4&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;[10, 15, 3, 7], k=17&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Yes, since 10+7 = 17&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;[5, 4, 7, 12, 1], k = 2&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No&lt;/p&gt;

&lt;h2&gt;
  
  
  Solutions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Brute force
&lt;/h3&gt;

&lt;p&gt;A simple solution would be to iterate over all possible pairs in the array and checking if a pair adds up to k.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for i from 0 to n-1:
  for j from i+1 to n-1:
    if a[i] + a[j] == k:
      return true
return false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will involve &lt;code&gt;n(n+1)/2&lt;/code&gt; steps, so the time complexity will be &lt;code&gt;O(n^2)&lt;/code&gt;, and &lt;code&gt;O(1)&lt;/code&gt; space complexity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sorting and two-pointer approach
&lt;/h3&gt;

&lt;p&gt;We can sort the array and use two pointers, &lt;code&gt;front&lt;/code&gt; (starting from 0) and &lt;code&gt;end&lt;/code&gt; (starting from n-1):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If the elements pointed to currently sum up to &lt;code&gt;k&lt;/code&gt;, then return true&lt;/li&gt;
&lt;li&gt;If the sum is less than &lt;code&gt;k&lt;/code&gt;, increment the &lt;code&gt;front&lt;/code&gt; pointer, since we need to increase the sum and the array is sorted.&lt;/li&gt;
&lt;li&gt;Symmetrically, if the sum is more than &lt;code&gt;k&lt;/code&gt;, decrement the &lt;code&gt;end&lt;/code&gt; pointer.
We repeat this until &lt;code&gt;front&lt;/code&gt; and &lt;code&gt;end&lt;/code&gt; cross each other.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sort(a)
front = 0, end = n-1
while front &amp;lt; end:
  if a[front] + a[end] == k:
    return true
  else if a[front] + a[end] &amp;lt; k:
    front = front + 1
  else
    end = end - 1
return false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we are sorting an array (&lt;code&gt;O(nlogn)&lt;/code&gt;) and looping with the &lt;code&gt;front&lt;/code&gt; and &lt;code&gt;end&lt;/code&gt; pointers (&lt;code&gt;O(n)&lt;/code&gt;), the overall time complexity of this solution is (&lt;code&gt;O(nlogn)&lt;/code&gt;). The space complexity is (&lt;code&gt;O(1)&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;You can find the implementation of this solution &lt;a href="https://github.com/akshatshah21/Data-Structures-and-Algorithms/blob/master/C%2B%2B/Arrays/Check_2_sum.cpp"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we are required to return the indices, the sorting approach cannot be used directly. We will have to make an array of pairs of &lt;code&gt;(val, index)&lt;/code&gt; and then sort this array.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using a set or map
&lt;/h3&gt;

&lt;p&gt;We can iterate through the array and keep adding the elements to a set (or hashset, map or hashmap), and for every element, check if the set contains &lt;code&gt;(k-a[i])&lt;/code&gt;. If we find this condition to be true, then there exists a pair that adds up to &lt;code&gt;k&lt;/code&gt;: the current &lt;code&gt;a[i]&lt;/code&gt; and the entry in the set, &lt;code&gt;k-a[i]&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;set m
for i from 0 to n-1:
  if m.has(k-a[i]):
    return true
  m.add(a[i])
return false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are making a single pass through the array, finding whether a number exists in the set, and adding an element to the set. If we are using a BST implementation of set (C++ &lt;code&gt;set&lt;/code&gt; or &lt;code&gt;map&lt;/code&gt;) then the time complexity will be &lt;code&gt;O(nlogn)&lt;/code&gt;, since every insert/find operation takes &lt;code&gt;O(logn)&lt;/code&gt; time. If we use a hashset or hashmap (C++ &lt;code&gt;unordered_set&lt;/code&gt; or &lt;code&gt;unordered_map&lt;/code&gt;) then the time complexity will be &lt;code&gt;O(n)&lt;/code&gt;, since insert/find operation can be done in constant time.&lt;/p&gt;

&lt;p&gt;You can find the implementation of this solution &lt;a href="https://github.com/akshatshah21/Data-Structures-and-Algorithms/blob/master/C%2B%2B/Hashing%20or%20Maps/Check_Numbers_Add_Upto_k_in_Array.cpp"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we are required to return indices of these elements, then need to use a map or hashmap, with the key-value pairs as &lt;code&gt;(value, index)&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>algorithms</category>
    </item>
  </channel>
</rss>
