Jekyll2023-07-09T22:19:21+00:00http://www.ishaangandhi.com/writing/feed.xmlwritingTraffic2023-07-03T00:00:00+00:002023-07-03T00:00:00+00:00http://www.ishaangandhi.com/writing/2023/07/03/traffic<p>Drivers entering lower Manhattan will be charged a congestion fee starting next
year. The tax will be between $9 and $23 per vehicle and will ostensibly help
alleviate traffic between 60th Street and the southern tip of the island.</p>
<p>Even if you don’t own a car, you will still pay the tax indirectly. Most
groceries and Amazon orders are trucked in from New Jersey. Lyfts, Ubers, and
cabs will be pricier.</p>
<p>Unfortunately, I don’t think the tax will alleviate congestion. <strong>Most traffic
in Manhattan is not caused by the <em>number</em> of cars but by the carelessness of
their drivers.</strong> Drivers routinely block lanes or even entire streets by
stopping inappropriately.</p>
<p>The Manhattan grid has 60-foot-wide streets running roughly East-West, and
100-foot wide avenues running roughly North-South. Between the streets and the
avenues, the streets are the bigger culprit of traffic. They are poorly
utilized. Despite having the potential to support 3 lanes of traffic, only 1 is
actively used. The other 2 lanes are used for parking, garbage containers, and
restaurant sheds.</p>
<p>The 1 <em>actively</em> used lane easily gets blocked. The most common reasons are cabs
picking up and letting out passengers and trucks delivering to local businesses.
Everyone has seen a garbage truck crawl across a block with a parade of cars
stuck behind it. This is particularly problematic when the trucks are scheduled
to collect trash during rush hour; I see a sanitation truck cause 8 AM buildups
on 53rd Street every week.</p>
<p>The avenues fare slightly better since most of them have 5 lanes. 2 are used for
parking or restaurants, just like the streets. However, spillover from the
easily-blocked streets can halt avenues and quickly create gridlock.</p>
<p>The most heinous stop I’ve seen caused a 2-hour slowdown through all of SoHo,
across the Holland tunnel, and into New Jersey. A woman parked her car on I-78
and stood in an adjacent lane, talking on her phone. I have to assume the car
was stuck, but the fact that she was on her cell phone outside, blocking another
lane, was entirely out of pocket.</p>
<p>Drivers usually make these traffic-causing stops out of necessity rather than
convenience: there is no good place to make necessary stops without blocking
traffic. But it doesn’t have to be this way: you can imagine reserving the last
2 parking spots on every street as a stopping zone. That would be the only zone
where you can do deliveries, wait to pick up passengers, or pull over after a
fender-bender. That would significantly speed up traffic, in my view.</p>Drivers entering lower Manhattan will be charged a congestion fee starting next year. The tax will be between $9 and $23 per vehicle and will ostensibly help alleviate traffic between 60th Street and the southern tip of the island.What you should know about non-competes before signing one2023-03-31T00:00:00+00:002023-03-31T00:00:00+00:00http://www.ishaangandhi.com/writing/2023/03/31/nc<h3 id="context">Context</h3>
<p>A non-compete, short for a non-compete agreement, is a contract between you and your employer that, after leaving your job, you won’t work for a competitor for a certain period. Lawyers, security people, and sandwich makers have signed these agreements, though I am most familiar with non-competes in my own industry, finance. I have a non-compete as a software engineer at a high-frequency trading firm.</p>
<h3 id="purported-raison-detre">Purported raison d’etre</h3>
<p>Proponents of non-competes say they are necessary for protecting confidential or proprietary information. The argument goes that some firms have critical secrets (trading algorithms, manufacturing methods, recipes for sodas.) Firms obtain these secrets at great R&D expense, hoping they will increase the company’s profits. If employees could join competitors too soon after quitting, competitors would learn secrets without having to invest their own money. Perhaps firms would spend less and less on research, opting instead to hire knowledgeable employees from competitors. Progress would stagnate.</p>
<h3 id="are-they-really-necessary">Are they really necessary?</h3>
<p>I personally don’t find this argument very convincing.</p>
<p>Is it true that without strict non-competes, companies would be unable to protect secrets? Actually, intellectual property is protected by law in all 50 states by copyrights, patents, and trade-secret laws. These mechanisms protect secrets without limiting job mobility.</p>
<p>Furthermore, it’s possible to protect IP without taking competitors to court at all. All profitable firms are profitable because they have a competitive advantage, and in a knowledge economy, the competitive advantage is partially from the knowledge of their workers. Google, for example, has a competitive advantage in search engines partly because of its secret algorithms. Profitable firms that don’t have non-competes manage to preserve their competitive edge by simply paying their workers enough that they don’t leave. The average tenure at Google is between 1 and 3 years, but it has retained a monopoly in search for two decades while (mainly) avoiding non-competes.</p>
<p>Non-competes are ripe for abuse by the employer because they alone decide what counts as a competitor and what counts as a trade secret. Non-competes are frequently used even when there are no secrets to protect. For example, one employee in the high-frequency trading industry sought to accept a job helping low-income workers with their resumes and interview skills. Her hedge fund employer prevented her from taking this job because they said the resume editing company was a competitor. Because legal fees would be so expensive, she was effectively left with no recourse.</p>
<h3 id="non-compete-lengths">Non-compete lengths</h3>
<p>I’ve left out one aspect of non-competes up til now. Non-competes lengths are stipulated as “maximums.” A typical agreement says, “You will not work for competitors for up to 18 months after leaving.” The actual length of the NC might be 0 months, 18 months, or anywhere in between. So how is this actual length determined?</p>
<p>Officially, on your last day of work, HR would review the projects I had worked on, and depending on how secret these projects are, HR would choose a length, up to 18 months, of NC. In reality, things work differently. From what I’ve observed, the four most significant factors that determine the length of your non-compete are:</p>
<ul>
<li>How popular you are at your firm (<em>Wei is a nice guy but burned out. Give him the entire length so he can recharge.</em>) This is non-compete as compensation for friends.</li>
<li>Where you want to work next (<em>George is leaving and going to RivalFirm. We want to hire Jack from RivalFirm, who has a long non-compete. Give George the full length. That way, we can work out an agreement with RivalFirm to reduce George’s non-compete if RivalFirm reduces Jack’s.</em> ***) This is non-compete as negotiating power.</li>
<li>How prestigious your manager wants to seem (<em>My employee is leaving. Tell HR to give him the entire length. Otherwise, my boss won’t think the team I manage does important work.</em>) This is non-compete as empire-building.</li>
<li>Whether you are still needed on the team (<em>Jessica handed in her resignation, but we need all the help we can get right now. Tell her that if she quits, we will enforce a 24-month non-compete, but if she works for another 12 months, we will only enforce 12 months.</em>) This is non-compete as a deterrent to quitting.</li>
</ul>
<p>Protecting secrets, the purported reason non-competes exist, is not on the list.</p>
<hr />
<p>** When joining the firm, I signed a non-compete of 12 months. Four weeks into the job, my employer told me I would need to sign a new, 24-month, non-compete or I would be fired. I signed.</p>
<p>*** Yes, this really is the way it works (at my company, at least).</p>ContextWhy you should do programming interviews in C++2023-03-30T00:00:00+00:002023-03-30T00:00:00+00:00http://www.ishaangandhi.com/writing/2023/03/30/cpp-interviews<p>I used Python for interviews for a few years, but in 2020, I switched to C++ and haven’t looked back. Since switching to C++, I’ve passed interviews at major tech companies like Google and Databricks and financial firms like Jane Street and Headlands Tech GM. I attribute much of my success in passing these interviews to my choice of language.</p>
<p>It’s conventional wisdom that you should do programming interviews in Python. The argument goes that because Python has a concise syntax and a powerful standard library, you can write solutions to interview problems faster in Python than in any other language.</p>
<p>This advice misses the mark. The point of interviews is not to finish the problem; it’s to convince your interviewer that you would be a successful engineer at their company. From that perspective, C++ shines.</p>
<h2 id="c-is-a-strongly-typed-language">C++ is a strongly typed language</h2>
<p>C++ is a strongly typed language, meaning every variable has a specific data-type checked at compile-time. Writing explicit types in my code makes it easier for interviewers to follow my thought process. The fundamental issue with Python variable declarations is that when you declare a variable or function, your interviewer won’t know its type, and will have a hard time determining its purpose.</p>
<p>Contrast these Python declarations:</p>
<pre><code class="language-Python">index_to_range = {}
</code></pre>
<p>with this C++ declaration:</p>
<pre><code class="language-C++">map<int, pair<int, int>> indexToRange;
</code></pre>
<p>Which variable is easier to understand? When interviewers can easily follow your thought process, you will seem more logical and competent.</p>
<p>Additionally, explicit types make it harder to write incorrect code. One mistake I would commonly make when writing solutions in Python was inserting the wrong type into a dictionary. Now that I use C++, this mistake seldom happens. If the shared editor used for interviews has intellisense, a red squiggle appears under incorrectly typed code, and I can fix the error. However, even without intellisense, I find it much easier to avoid mistakes if every variable declaration also includes a type.</p>
<h2 id="c-lends-you-an-air-of-seriousness">C++ lends you an air of seriousness</h2>
<p>I once had an interviewer ask me to write a function to reverse a string. It took me a few seconds to write down this answer:</p>
<pre><code class="language-Python">def reverse(s):
return s[::-1]
</code></pre>
<p>He was not pleased with my answer. “Uh… Ok, I mean… That’s right, but… Can you write that in a <em>real</em> programming language?”</p>
<p>Me: “Python <em>is</em> a real language.”</p>
<p>Him: “I mean, a serious language… can you write it in C?”</p>
<p>I wrote a solution in C and passed that interview. It was unprofessional, and frankly incorrect for the interviewer to call my answer “unserious” or not “real,” but this kind of disrespect for Python code is a typical attitude. The only thing unusual about this interviewer was that he was explicit about his bias.</p>
<p>Python’s builtins and standard library functions make writing solutions to programming problems quick. However, when using these aids, you don’t get to demonstrate <em>your</em> ability to write complex, intricate, careful code. Remember: the point of interviews is <strong>not</strong> to finish the problem. It is to show your interviewer you would be successful at their company.</p>
<h2 id="cs-verbosity-is-not-an-issue-when-using-text-editors">C++’s verbosity is not an issue when using text editors</h2>
<p>Pre-Covid, when candidates wrote their code on a whiteboard during interviews, choosing a language other than Python for technical interviews made very little sense. However, most interviews are now conducted over Zoom via shared text editors like Coderpad.</p>
<p>Consider that a typical programming question may require ~20 lines of code to answer in either Python or C++. The Python code may require ~4 words per line, and the C++ code may need ~10 words per line. Assuming a typing speed of 60 words per minute, typing these solutions takes 1 minute and 20 seconds with Python and 3 minutes and 20 seconds with C++. Saving an extra 2 minutes by typing less almost never makes the difference between success and failure in the typical hour-long interview.</p>
<p>You can see for yourself just how little the C++ verbosity handicap matters by watching <a href="https://youtu.be/EuPSibuIKIg?t=116">this mock interview</a> with competitive programmer Errichto. In the video, Errichto expertly completes a challenging interview question in ~3 minutes of C++ coding.</p>
<h2 id="c-has-somewhat-obscure-syntax-and-odd-semantics">C++ has somewhat obscure syntax and odd semantics</h2>
<p>There is a running joke that Python is “just English” or “just pseudocode.” In my experience, the friendliness of Python syntax makes interviewers overconfident in their ability to evaluate your code. For example, in one interview I had with Google, I wrote a Python code similar to the following:</p>
<pre><code class="language-Python">def foo(bar: str):
for i in range(len(bar)):
# Do something that takes O(1) time
</code></pre>
<p>I was asked to provide the time complexity of <code class="language-plaintext highlighter-rouge">foo</code>, which is <code class="language-plaintext highlighter-rouge">O(N)</code>. My interviewer, who was not a Python programmer, insisted that the code had complexity <code class="language-plaintext highlighter-rouge">O(N^2)</code>. He believed the <code class="language-plaintext highlighter-rouge">len(bar)</code> function call took <code class="language-plaintext highlighter-rouge">O(N)</code> time and executed every loop iteration. Of course, any Python programmer knows this is not how generators work in Python, but just looking at the code, I can see how a Java or Go developer might think that.</p>
<p>By contrast, C++ code can simply <em>look</em> so odd that interviewers will give you the benefit of the doubt on code you write. If they think there may be a mistake in your code, they tend to assume <em>your</em> code is correct and that <em>they</em> are the ones who just don’t understand C++ well enough to judge your answer.</p>I used Python for interviews for a few years, but in 2020, I switched to C++ and haven’t looked back. Since switching to C++, I’ve passed interviews at major tech companies like Google and Databricks and financial firms like Jane Street and Headlands Tech GM. I attribute much of my success in passing these interviews to my choice of language.Checklist for coding interviews2023-02-28T00:00:00+00:002023-02-28T00:00:00+00:00http://www.ishaangandhi.com/writing/2023/02/28/checklist<p>During coding interviews, after you have finished your initial implementation of the question, your interviewer might ask you <em>“are you finished with this part?”</em></p>
<p>Instead of saying yes immediately, try the following:
<em>“I think I am, but can you give me a minute to check over the code?”</em></p>
<p>Using that minute to follow this checklist will show your interviewer you care about readability and can help you find common bugs.</p>
<ol>
<li>
<p><strong>Choose self-describing variable and function names</strong></p>
<p>For variable names, string together 2-4 words to describe the data. For example, <code class="language-plaintext highlighter-rouge">activeUsers</code> or <code class="language-plaintext highlighter-rouge">indexOfMaxHeight</code>. I like to use variables similar to <code class="language-plaintext highlighter-rouge">numFoo</code> or <code class="language-plaintext highlighter-rouge">isBar</code> for variables representing counts and booleans, respectively. For functions, use the “verb + noun” pattern. For example, <code class="language-plaintext highlighter-rouge">getThing()</code> or <code class="language-plaintext highlighter-rouge">isBar()</code>.</p>
<p>Generally, avoid single-character names like <code class="language-plaintext highlighter-rouge">x</code>, unless you use <code class="language-plaintext highlighter-rouge">i</code> as a loop index for a small (2-5 line) singly-nested loop.</p>
</li>
<li>
<p><strong>Choose appropriate data structures</strong></p>
<p>Every data structure has a prototypical element access style, query to run against the data, or both. Here is a non-exhaustive list.</p>
<table>
<thead>
<tr>
<th>Data structure</th>
<th>Traversal pattern</th>
<th>Query</th>
</tr>
</thead>
<tbody>
<tr>
<td>Queue</td>
<td>Accessing the single least recently added item</td>
<td>-</td>
</tr>
<tr>
<td>Stack</td>
<td>Accessing the single most recently added item</td>
<td>-</td>
</tr>
<tr>
<td>Vector</td>
<td>Accessing arbitrary elements by index</td>
<td>-</td>
</tr>
<tr>
<td>Heap</td>
<td>-</td>
<td>What is the smallest element contained?</td>
</tr>
<tr>
<td>Map</td>
<td>Access all key-value pairs in arbitrary order</td>
<td>What is the value associated with this key?</td>
</tr>
<tr>
<td>Set</td>
<td>Accessing all elements in an arbitrary order</td>
<td>Is this item in the data?</td>
</tr>
</tbody>
</table>
<p>If you are accessing data from one of these data structures in a way other than the way listed in the table, you likely have chosen the wrong data structure. For example, if you only access the back of a vector and never access an element by its index, consider using a stack instead.</p>
<p>Similarly, if you find yourself running a query like “is this item in the data” on a heap, consider using a set instead since membership tests are its raison-detre.</p>
</li>
<li>
<p><strong>Ensure code within the same function is at the same level of abstraction.</strong></p>
<p>Consider this code:</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kt">bool</span> <span class="nf">hasOverdueBooks</span><span class="p">(</span><span class="n">ID</span> <span class="n">userId</span><span class="p">)</span> <span class="p">{</span>
<span class="n">set</span><span class="o"><</span><span class="n">Loan</span><span class="o">></span> <span class="n">usersLoans</span> <span class="o">=</span> <span class="n">getLoansForUser</span><span class="p">(</span><span class="n">userId</span><span class="p">);</span>
<span class="k">for</span> <span class="p">(</span><span class="k">const</span> <span class="k">auto</span><span class="o">&</span> <span class="n">loan</span> <span class="o">:</span> <span class="n">usersLoans</span><span class="p">)</span> <span class="p">{</span>
<span class="n">string</span> <span class="n">dueYear</span> <span class="o">=</span> <span class="n">atoi</span><span class="p">(</span><span class="n">loan</span><span class="p">.</span><span class="n">dueDate</span><span class="p">.</span><span class="n">substr</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">4</span><span class="p">));</span>
<span class="n">string</span> <span class="n">dueMonth</span> <span class="o">=</span> <span class="n">atoi</span><span class="p">(</span><span class="n">loan</span><span class="p">.</span><span class="n">dueDate</span><span class="p">.</span><span class="n">substr</span><span class="p">(</span><span class="mi">5</span><span class="p">));</span>
<span class="k">if</span> <span class="p">(</span><span class="n">dueYear</span> <span class="o"><</span> <span class="n">currentYear</span><span class="p">())</span>
<span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">dueYear</span> <span class="o">==</span> <span class="n">currentYear</span><span class="p">()</span> <span class="o">&&</span> <span class="n">dueMonth</span> <span class="o"><</span> <span class="n">currentMonth</span><span class="p">())</span>
<span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div> </div>
<p>This code is hard for your interviewer to follow because it uses three distinct levels of abstraction in the same function. The first line speaks in the langauge of business objects - <code class="language-plaintext highlighter-rouge">user</code> and <code class="language-plaintext highlighter-rouge">loan</code> objects. The first two lines of the for-loop speak the language of string manipulation. The last four lines of the loop speak the language of date comparisons.</p>
<p>Split this into three helper functions to match the three levels of abstraction.</p>
<div class="language-c++ highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">pair</span><span class="o"><</span><span class="kt">int</span><span class="p">,</span> <span class="kt">int</span><span class="o">></span> <span class="n">parseYearAndMonth</span><span class="p">(</span><span class="k">const</span> <span class="n">string</span><span class="o">&</span> <span class="n">s</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Code in this function uses "string" abstractions</span>
<span class="kt">int</span> <span class="n">year</span> <span class="o">=</span> <span class="n">atoi</span><span class="p">(</span><span class="n">s</span><span class="p">.</span><span class="n">substr</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">4</span><span class="p">));</span>
<span class="kt">int</span> <span class="n">month</span> <span class="o">=</span> <span class="n">atoi</span><span class="p">(</span><span class="n">s</span><span class="p">.</span><span class="n">substr</span><span class="p">(</span><span class="mi">5</span><span class="p">));</span>
<span class="k">return</span> <span class="p">{</span><span class="n">year</span><span class="p">,</span> <span class="n">month</span><span class="p">};</span>
<span class="p">}</span>
<span class="kt">bool</span> <span class="nf">isLoanOverdue</span><span class="p">(</span><span class="k">const</span> <span class="n">Loan</span> <span class="o">&</span><span class="n">loan</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Code in this function uses "date" abstractions</span>
<span class="n">pair</span><span class="o"><</span><span class="kt">int</span><span class="p">,</span> <span class="kt">int</span><span class="o">></span> <span class="n">dueYearAndMonth</span> <span class="o">=</span> <span class="n">parseYearAndMonth</span><span class="p">(</span><span class="n">loan</span><span class="p">.</span><span class="n">dueDate</span><span class="p">);</span>
<span class="k">return</span> <span class="n">dueYearAndMonth</span> <span class="o"><</span> <span class="n">make_pair</span><span class="p">(</span><span class="n">currentYear</span><span class="p">(),</span> <span class="n">currentMonth</span><span class="p">());</span>
<span class="p">}</span>
<span class="kt">bool</span> <span class="nf">hasOverdueBooks</span><span class="p">(</span><span class="n">ID</span> <span class="n">userId</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Code in this function uses "library" abstractions</span>
<span class="n">set</span><span class="o"><</span><span class="n">Loan</span><span class="o">></span> <span class="n">usersLoans</span> <span class="o">=</span> <span class="n">getLoansForUser</span><span class="p">(</span><span class="n">userId</span><span class="p">);</span>
<span class="k">return</span> <span class="n">any_of</span><span class="p">(</span><span class="n">usersLoans</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">usersLoans</span><span class="p">.</span><span class="n">end</span><span class="p">(),</span> <span class="n">isLoanOverdue</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div> </div>
<p>Our new code is the same length as the old code, but can be more easily read.</p>
</li>
<li>
<p><strong>Verify const-correctness</strong></p>
<p>If you can add <code class="language-plaintext highlighter-rouge">const</code> in front of variables or parameters, do so.</p>
</li>
<li>
<p><strong>Avoid magic numbers</strong></p>
<p>Replace these with well-named constants.</p>
</li>
<li>
<p><strong>Trace through the parts of code similar to <code class="language-plaintext highlighter-rouge">index + 1</code> or <code class="language-plaintext highlighter-rouge">index - 1</code>.</strong></p>
<p>This is a typical pattern when accessing the previous or next element in a collection, but it is error-prone.</p>
</li>
<li>
<p><strong>Make sure you handle the “empty input” edge case</strong></p>
</li>
<li>
<p><strong>Offer to write a few tests for your code</strong></p>
<p>If you’ve checked the previous seven steps items and are ready to move on, try:
<em>“Should I write some tests for this function?”</em></p>
<p>Your interviewer would probably have asked you to write a test anyways and will appreciate your taking the initiative. Remember the acronym <strong>ZOMBIE</strong> (zero, one, many, etc) when writing a comprehensive set of tests. You can learn more about that <a href="https://hackernoon.com/zombie-testing-one-behavior-at-a-time-9s2m3zjo#:~:text=Pick%20Zombie%20representant,Scenarios%2C%20Simple%20Solutions">here</a>.</p>
<p>Some firms don’t let you write tests. If you are interviewing at such a place, offer to instead trace through your code for a small input.</p>
</li>
</ol>During coding interviews, after you have finished your initial implementation of the question, your interviewer might ask you “are you finished with this part?”