<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" 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"><channel><title>Akash Manohar</title><link>https://www.define.run/</link><description>Recent content on Akash Manohar</description><atom:link href="https://www.define.run/index.xml" rel="self" type="application/rss+xml"/><item><title>Debugging Firebase events with Swift iOS apps</title><link>https://www.define.run/posts/debugging-firebase-events/</link><pubDate>Fri, 10 Jan 2025 23:14:34 +0700</pubDate><guid>https://www.define.run/posts/debugging-firebase-events/</guid><description> Notes on debugging Firebase events in Swift iOS apps</description><content:encoded><![CDATA[ <blockquote>
<p>This post assumes that a Firebase project has been setup with Google Analytics enabled.</p>
</blockquote>
<p>To be able to debug Firebase events in Swift apps, Firebase requires certain arguments to be passed when running the app. The details are available in the <a href="https://firebase.google.com/docs/analytics/debugview">Firebase docs</a>.</p>
<p>In xcode, the <code>-FIRDebugEnabled</code> option has to be added to the Scheme that is being used to build the app.</p>
<ol>
<li>
<p>In the area where you see the current scheme and the device/simulator used to run the app, click on the scheme to reveal a menu with the option to edit the scheme.</p>
</li>
<li>
<p>Find the &ldquo;Run&rdquo; option in the sidebar for the Debug release.</p>
</li>
<li>
<p>Find the &ldquo;Arguments&rdquo; tab in this window.</p>
</li>
<li>
<p>Add <code>-FIRDebugEnabled</code> as the arguments to be passed for launch. The hyphen is important :)</p>
</li>
</ol>
<p><img src="https://www.define.run/images/posts/debugging-firebase-events/xcode-edit-scheme.png" alt="xcode edit scheme"></p>
<h2 id="testing-firebase-events">Testing Firebase events</h2>
<p>Firebase events can be tested on the Google Analytics project connected to Firebase. Login to Google Analytics and find the Google Analytics property associated with the Firebase project.</p>
<p>In the Google Analytics property, under the admin panel, find the <em>Data Display</em> menu option, and then the <em>Debug View</em> under it.</p>
<p><img src="https://www.define.run/images/posts/debugging-firebase-events/google-analytics-debug-view.png" alt="View firebase events in google analytics"></p>
<p>Run your app in xcode, either in a simulator or an iOS device. While you use the device, the events should show up in the Google Analytics debug view.</p>
<blockquote>
<p><strong>Important:</strong> The events are only logged in the Debug View when the app is run via xcode.</p>
</blockquote>
]]></content:encoded></item><item><title>The Missing Guide to AWS SDK for Swift</title><link>https://www.define.run/posts/aws-sdk-swift/</link><pubDate>Thu, 14 Jul 2022 03:40:59 +0700</pubDate><guid>https://www.define.run/posts/aws-sdk-swift/</guid><description> Notes on using the aws-sdk-swift project in your macOS or iOS project.</description><content:encoded><![CDATA[ <p>I tried the <a href="https://github.com/awslabs/aws-sdk-swift">AWS SDK for Swift</a> for one of the projects I was working on. Here are some notes to supplement with the minimal documentation the project has.</p>
<blockquote>
<p>The latest version of the SDK as of this guide is 0.2.5. This guide should work for both iOS and macOS since the AWS libraries are <a href="https://aws.amazon.com/about-aws/whats-new/2021/12/aws-sdk-swift-developer-preview/">said to be platform agnostic</a>.</p>
</blockquote>
<h2 id="adding-the-sdk-to-a-swift-project">Adding the SDK to a Swift project</h2>
<p>To add the package to the project in XCode, use the <em>Add Packages</em> option in the <em>File</em> menu. In the window that appears, enter the GitHub URL of the project to discover AWS packages.</p>
<pre tabindex="0"><code>https://github.com/awslabs/aws-sdk-swift
</code></pre><p>When prompt appears asking to choose the libraries to add, take your pick. For this example, we will be using the &ldquo;CloudWatch Logs&rdquo; client.</p>
<h2 id="ensure-build-targets-have-dependencies">Ensure build targets have dependencies</h2>
<p>I created a universal app and the dependencies were added only to the iOS app. It would be good to open the project window on XCode and check that the dependencies are listed for each of the targets.</p>
<p>My project has macOS and iOS as targets and I&rsquo;ve ensured that the dependencies are in both. This section in the latest version of XCode is called &ldquo;Frameworks, Libraries and Embedded Content&rdquo;. If the libraries are missing, click on the plus sign and add them.</p>
<h2 id="import-the-libraries">Import the libraries</h2>
<p>As you type in class names, XCode would automatically suggest the libraries to import. So do not bother much about finding these manually for the libraries that you use. These are the XCode suggested imports for my code (and they work as expected 😃).</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#a6e22e">AWSCloudWatchLogs</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#a6e22e">AWSClientRuntime</span>
</span></span></code></pre></div><h2 id="create-a-credentials-provider-config">Create a Credentials Provider config</h2>
<p>All clients for the AWS Services in the SDK require a &ldquo;credentials provider&rdquo; object to be passed as input. This is just a fancy label for any kind of AWS credentials.</p>
<p>The most common credentials are the AWS Access Key and AWS Secret Key. the below snippet creates a new object with the relevant credentials.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#66d9ef">let</span> aws_config = AWSCredentialsProviderStaticConfig(
</span></span><span style="display:flex;"><span>    accessKey: <span style="color:#e6db74">&#34;REPLACE_THIS&#34;</span>,
</span></span><span style="display:flex;"><span>    secret: <span style="color:#e6db74">&#34;REPLACE_THIS&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><h2 id="create-a-client-for-the-aws-service">Create a client for the AWS Service</h2>
<p>The next bunch of steps</p>
<ol>
<li>Create a config for the client we need (CloudWatch Logs in this case).</li>
<li>Use the config to create a client for the AWS Service.</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#66d9ef">let</span> client_config = <span style="color:#66d9ef">try</span> CloudWatchLogsClient.CloudWatchLogsClientConfiguration(
</span></span><span style="display:flex;"><span>    region: <span style="color:#e6db74">&#34;us-west-2&#34;</span>,
</span></span><span style="display:flex;"><span>    credentialsProvider: AWSCredentialsProvider.fromStatic(aws_config)
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> client = CloudWatchLogsClient(config: client_config)
</span></span></code></pre></div><h2 id="make-a-request-to-fetch-log-groups">Make a request to fetch log groups</h2>
<p>Payload for requests to AWS services are organized as input structs for different operations. For this operation, our input struct is <code>DescribeLogGroupsInput</code>. We do not have any special options to pass, so this will be an empty struct.</p>
<p>Our helper method is actually <code>describeLogGroups</code> according to the docs. If we did that the result would be available as <code>response.logGroups</code>. The snippet below has a bit more magic.</p>
<h3 id="the-paginated-helper">The &ldquo;Paginated&rdquo; helper</h3>
<p>Most AWS SDKs now have auto-pagination. If we used <code>describeLogGroups</code> and there was more than one page of values, then the API response would include the first page of values, along with a pageToken as a reference to the net page to query.</p>
<p>If we used the <code>describeLogGroupsPaginated</code> function, then the SDK makes requests to fetch all values across pages in a loop (on-demand, you&rsquo;ll see below). This is implemented with Swift&rsquo;s new <code>AsyncSequence</code> protocol (<a href="https://developer.apple.com/videos/play/wwdc2021/10058/">WWDC 2021 video</a>)</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#66d9ef">let</span> response = client.describeLogGroupsPaginated(
</span></span><span style="display:flex;"><span>  input: DescribeLogGroupsInput()
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>To print the responses, we could use a loop.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#75715e">// Each item in the response would be a page of values.</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// So we loop again to print each log event.</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> <span style="color:#66d9ef">try</span> await page <span style="color:#66d9ef">in</span> response {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#66d9ef">let</span> logGroups = page.logGroups {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">for</span> logGroup <span style="color:#66d9ef">in</span> logGroups {
</span></span><span style="display:flex;"><span>            print(<span style="color:#e6db74">&#34;--- Received log line ---&#34;</span>)
</span></span><span style="display:flex;"><span>            print(logGroup)
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><em>AFAIK, any helper function in the AWS SDK that lists values/paginages would support another helper with the &ldquo;Paginated&rdquo; suffix like above.</em></p>
<h2 id="the-final-piece">The final piece</h2>
<p>As our code has a few areas that might throw errors, we wrap all of it in a do-catch block for now.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#66d9ef">do</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">let</span> client_config = <span style="color:#66d9ef">try</span> CloudWatchLogsClient.CloudWatchLogsClientConfiguration(
</span></span><span style="display:flex;"><span>        region: <span style="color:#e6db74">&#34;us-west-2&#34;</span>,
</span></span><span style="display:flex;"><span>        credentialsProvider: AWSCredentialsProvider.fromStatic(aws_config)
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">let</span> client = CloudWatchLogsClient(config: client_config)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">let</span> response = client.describeLogGroupsPaginated(
</span></span><span style="display:flex;"><span>      input: DescribeLogGroupsInput()
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    print(<span style="color:#e6db74">&#34;Printing output...&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#66d9ef">try</span> await page <span style="color:#66d9ef">in</span> response {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#66d9ef">let</span> logGroups = page.logGroups {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">for</span> logGroup <span style="color:#66d9ef">in</span> logGroups {
</span></span><span style="display:flex;"><span>                print(<span style="color:#e6db74">&#34;--- Received log line ---&#34;</span>)
</span></span><span style="display:flex;"><span>                print(logGroup)
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    print(<span style="color:#e6db74">&#34;-#-#- Call to AWS complete&#34;</span>)
</span></span><span style="display:flex;"><span>} <span style="color:#66d9ef">catch</span> {
</span></span><span style="display:flex;"><span>    print(<span style="color:#e6db74">&#34;AWS init error&#34;</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="next-steps">Next steps</h2>
<p>I have not yet tried to call a helper function that creates/destroys resources. So this post does not have any examples on how to handle the response for that. Will update this post in the future.</p>
]]></content:encoded></item><item><title>Using Cloudflare Tunnels to expose localhost to the internet</title><link>https://www.define.run/posts/cloudflare-tunnels/</link><pubDate>Sun, 20 Feb 2022 00:00:00 +0000</pubDate><guid>https://www.define.run/posts/cloudflare-tunnels/</guid><description> &lt;p>Cloudflare Tunnels is the newest alternative to tools like Ngrok and localtunnel. These tools help expose locally hosted apps and websites to the internet.&lt;/p></description><content:encoded><![CDATA[ <p>Cloudflare Tunnels is the newest alternative to tools like Ngrok and localtunnel. These tools help expose locally hosted apps and websites to the internet.</p>
<p>On Cloudflare Tunnels, tunnels with permanent domains are free. This is a good because it makes working with or debugging webhooks with local apps very easy.</p>
<h2 id="install-cloudflared">Install cloudflared</h2>
<p>Install <code>cloudflared</code>  with homebrew like below or checkout <a href="https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation">install instructions for other operating systems</a>)</p>
<pre tabindex="0"><code>brew install cloudflare/cloudflare/cloudflared
</code></pre><h2 id="start-a-tiny-web-server-to-test">Start a tiny web server to test</h2>
<p>Create and start a tiny web server to tryout cloudflared.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>mkdir tunnel-tryouts <span style="color:#f92672">&amp;&amp;</span> cd tunnel-tryouts
</span></span><span style="display:flex;"><span>echo <span style="color:#e6db74">&#34;&lt;html&gt;&lt;h1&gt;Hello Cloudflare Tunnel&lt;/h1&gt;&lt;/html&gt;&#34;</span> &gt; index.html
</span></span><span style="display:flex;"><span>python3 -m http.server
</span></span></code></pre></div><p>The website we just brought up should be accessible at http://localhost:8000/</p>
<p>The output should look something like below.</p>
<p><img src="https://www.define.run/python-server.png" alt="python3 http server"></p>
<h2 id="start-a-cloudflare-tunnel">Start a Cloudflare Tunnel</h2>
<p>To make this available to the world, run the cloudflared command by passing the url of the website as an argument.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>cloudflared tunnel --url http://localhost:8000/
</span></span></code></pre></div><p>The output should look like something below. The tunnel url is also provided in the output. Hit the tunnel url and the website should be accessible to the world.</p>
<p><img src="https://www.define.run/cloudflared-output.png" alt="Hello Cloudflare Tunnel"></p>
<h2 id="running-tunnels-with-permanent-domains-or-subdomains">Running tunnels with permanent domains or subdomains</h2>
<p>This requires a domain on your Cloudflare account that you can use for the tunnel.</p>
<h4 id="login-to-cloudflare-and-authorise-use-of-one-of-the-domains">Login to Cloudflare and authorise use of one of the domains</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>cloudflared tunnel login
</span></span></code></pre></div><p>You will be prompted to authorize one of your domains to use for your tunnels. Pick one and that completes the setup.</p>
<p><img src="https://www.define.run/cloudflare-authorize.png" alt="Authorize cloudflare to use a domain for the tunnels"></p>
<h4 id="start-a-tunnel-with-the-required-domain-or-subdomain">Start a tunnel with the required domain or subdomain.</h4>
<p>I am using one of my domains to play with.</p>
<pre tabindex="0"><code>cloudflared tunnel --hostname myapp.define.run --url http://localhost:8000
</code></pre><p>The website should now be accessible at the desired subdomain 😃</p>]]></content:encoded></item><item><title>Attention to detail as your north star</title><link>https://www.define.run/posts/details/</link><pubDate>Fri, 04 Feb 2022 01:19:09 +0530</pubDate><guid>https://www.define.run/posts/details/</guid><description> This post is in response to a Hacker News thread about being slow to produce work.</description><content:encoded><![CDATA[ <blockquote>
<p>This post is in response to a <a href="https://news.ycombinator.com/item?id=30191017">Hacker News</a> thread about being slow to produce work.</p>
</blockquote>
<p>The kind of person the author seems to envy in the post are people who are most likely pattern-matching against past experience. I attribute this quick thinking to having come across those same/similar problem statements earlier. But getting there requires a good understanding of the problem space. This can only be done by spending time in the problem space and paying attention to details.</p>
<p>When working on a new problem these days. I feel like I slow down too. I now tend to go for the details. What I cannot make up in speed, I make up for with detailed solutions, thinking from the user&rsquo;s perspective, staying aware of trade-offs, watching out for unhandled scenarios, etc.</p>
<p>I also document all my work. Whenever I start working on a problem/ticket/issue, I create a new note in my personal note-taking tool. I document the commands, the new findings, etc.</p>
<p><strong>Fun fact:</strong> For a limited time, I fulfilled the role of a Product Manager at my recent workplace. The engineers I worked with loved the amount of detailing in my product specifications. This was the result of slowing down and paying attention to the details. The above qualities/choices also resulted in me playing the implicit role of QA for the team. The concept of &ldquo;implicit roles&rdquo; is explained pretty well in <a href="https://staysaasy.com/management/2021/01/21/Step-Back.html">this blog post</a> on the StaySassy blog.</p>
<p>I try to compensate my slowness with attention to detail, clear communication and better documentation.</p>
<p>Attention to detail is as good a quality as quick thinking. I like this way better.</p>
]]></content:encoded></item><item><title>Questions to ask potential employers</title><link>https://www.define.run/posts/questions-for-potential-employers/</link><pubDate>Mon, 29 Nov 2021 22:12:03 +0530</pubDate><guid>https://www.define.run/posts/questions-for-potential-employers/</guid><description> You will be working for the company. But will the company work for you? If you are interviewing with companies, these notes should help ask questions and know what is in it for you.</description><content:encoded><![CDATA[ <p>You will be working for the company. But will the company work for you? If you are interviewing with companies, these notes should help ask questions and know what is in it for you.</p>
<h2 id="ask-to-know-more-about-product--roadmap">Ask to know more about product &amp; roadmap</h2>
<p>Use this information to understand/guesstimate if the company has the potential to increase revenue.</p>
<h4 id="ask-for-a-product-demo">Ask for a product demo</h4>
<ul>
<li>Getting to know the product in detail helps.</li>
<li>Also ask for login credentials for a test account to play with.</li>
</ul>
<h4 id="what-is-the-product-roadmap">What is the product roadmap?</h4>
<ul>
<li>How do items get into the roadmap and how do they get prioritized?</li>
<li>To check if the product development is stagnant. <a href="https://en.wikipedia.org/wiki/Busy_work">Busy work</a> is bad work.</li>
</ul>
<h4 id="who-are-the-target-customers-now-is-the-company-planning-to-focus-on-other-cohorts-of-customers">Who are the target customers now? Is the company planning to focus on other cohorts of customers?</h4>
<ul>
<li>Use this to check/guess the possible size of market using other sources.</li>
<li>For example: If target customers are small businesses that sell clothing, find other industry reports that offer insights/numbers for potential market size that this company can possibly grow to serve.</li>
</ul>
<h4 id="what-was-last-years-product-roadmap-and-what-was-worked-on-last-year">What was last year’s product roadmap and what was worked on last year?</h4>
<ul>
<li>Helps to know how often the company changes priorities.</li>
<li>If priority changes are too often, then focus gets scattered for product engineering.</li>
</ul>
<blockquote>
<p>It is very important to understand that product development can be a hit or miss during the early stages of a company. So some things might be in flux. Just not everything can be in flux.</p>
</blockquote>
<h2 id="special-note-frequently-changing-roadmap">Special note: Frequently changing roadmap</h2>
<p>If the product roadmap is changing frequently (quarter/half-yearly) in major ways or if there is frequent reprioritisation, then it means there are too many conflicting sub-purposes within the organization. There can be many reasons, some mentioned below.</p>
<ul>
<li><strong>Selling to bad-fit customers</strong>: A lot of deals require customization that forces trading-off already planned roadmap items. These customizations are very good if they are reusable - means sales is able to utilize the customizations to sell to more customers that need these. These trade-offs come at the cost of product stability. The more code written for specific customers, the larger and unmaintainable the code-base becomes. This in turn leads to increased technical debt and slower development.</li>
<li><strong>Lack of Product-Market fit</strong>: Company scaled to $X in revenue selling to a cohort of customers. But new cohort of customers require a bunch of other features that do not exist today.</li>
<li><strong>Lack of focus to iterate/improve existing functionality:</strong> This happens as a result of focusing too much on building new things.</li>
</ul>
<h2 id="find-out-about-plans-for-the-role-being-hired-for">Find out about plans for the role being hired for</h2>
<h4 id="what-will-be-the-responsibilities-of-the-role-for-the-first-year">What will be the responsibilities of the role for the first year?</h4>
<ul>
<li>Based on responsibilities of the role and the product roadmap, decide if the role being hired for is important enough for you to work and grow there.</li>
<li>For example, if being hired for as a Mobile Engineer and mobile apps are low on the roadmap, then the role is likely to be at risk in the near future after the project is done.</li>
<li>If the company cannot list out work for at least a year, then the company is better-off hiring a contractor for this role instead of a full-time employee.</li>
</ul>
<h4 id="what-are-the-expected-responsibilities-next-year">What are the expected responsibilities next year?</h4>
<ul>
<li>Check to know if anyone has at least given some thought to a career growth map for this role.</li>
<li>For example: I join as a Support Engineer. Is this a dead-end role? Does the organization expect that I work this role for more than two years? How am I going to grow?</li>
</ul>
<h4 id="does-the-organization-help-employees-upskill">Does the organization help employees upskill?</h4>
<ul>
<li>Access to learning resources, conferences, etc)</li>
<li>As an employee, I am going to spend significant amount of time working for the organization. Does the organization help me upskill in some ways?</li>
</ul>
<h4 id="are-there-enough-supporting-functions-for-the-role-to-succeed">Are there enough supporting functions for the role to succeed?</h4>
<ul>
<li>For example: For a mobile engineer position, is the product line important? Is there a product manager assigned?</li>
</ul>
<h2 id="ask-details-to-understand-esops-better">Ask details to understand ESOPs better</h2>
<p>Someone I know articulated it better: <em>&ldquo;Salary is for today&rsquo;s work. ESOPs are for stickiness&rdquo;</em>. Stickiness is what encourages an employee to stay invested in the company rather than switching workplaces after a year.</p>
<p>The below details are good to discuss/ask on a call, but also ensure to get these on an email to keep these on record.</p>
<h4 id="what-is-the-current-fmv-of-the-share-the-exercise-price-and-the-total-number-of-shares-in-the-pool">What is the <em>current FMV</em> of the share, the <em>Exercise Price</em> and the total number of shares in the pool?</h4>
<ul>
<li>ESOPs are &ldquo;alloted&rdquo; to you by the company at regular intervals. This is like &ldquo;permission to buy&rdquo;. To own these, you must buy them at an exercise price.</li>
<li>People with ESOPs make money when the company is sold or goes for an IPO. To know what you would make as a &ldquo;profit&rdquo; in this sale, you must know the price you are allowed to buy at. This is the exercise price (or the strike price).</li>
<li>You may be offered an exercise price at a discount compared to the actual market value of the share. This market value of the share is the Fair Market Value (FMV).</li>
<li>FMV cannot be a random number based on what someone feels - companies generally do a 409A valuation with an auditor.</li>
<li>To aid in understanding valuation, also find out total number of shares in the pool. Useful to know, what percentage you are being assigned. Check common practices for early employees and the value you might add.</li>
<li>Read up on why <em>FMV</em> and <em>Exercise Price</em> are important to know. There is already a lot of literature on this.</li>
</ul>
<p>Based on the roadmap, the market size and the competition, if you think the company will increase revenue (say 4x or 10x) in the next few years, then use these numbers to see what your upside or financial gains will be. Ofcourse there might be dilution, liquidation preferences, etc, when the company raises a round of funding. But knowing these numbers atleast gives you information to guess ballpark numbers of what you would make as a financial gain.</p>
<h4 id="does-the-company-provide-liquidity-opportunities-for-employees">Does the company provide liquidity opportunities for employees?</h4>
<ul>
<li>ESOPs are good. But if there is no way to liquidate them to cash, then the ESOPs are worth paper-money and useless. Find out what are the company&rsquo;s plans for offering liquidity to employees with ESOPs.</li>
<li>When was the last liquidity opportunity and who was it offered to? (Example: Employees that stayed for 2yrs, 4yrs, etc).</li>
</ul>
<h4 id="does-the-company-offer-esops-with-a-10-year-exercise-window">Does the company offer ESOPs with a 10-year exercise window?</h4>
<ul>
<li>Guideline: Workplaces that have an exercise window of 30-90 days is a red-flag.</li>
<li>If you quit the company after 2yrs, all the shares you vested must be exercised within the &ldquo;exercise window&rdquo; after you quit (say 30-90 days depending on the company&rsquo;s policy). Else you forfeit the right to purchase the alloted shares.</li>
<li>This is bad because employees might not have the cash to purchase an asset (company shares) that do not have liquidity and comes with an unknown probablity of making a profit.</li>
<li>This is also bad because if you work for a company in a geography that has to adhere to &ldquo;Perquisite tax&rdquo; mentioned above, then you also have to pay the tax on the paper-profit that you have not yet made.</li>
<li>Having a long exercise window of 10 years is a good thing, because after the employee feels his potential/peak at the company is past, they have the oppurtunity to move on and still have 10 years to exercise the vested shares depending on the potential upside.</li>
<li>10-yr exercise window is a good expectation to have. Anyone saying otherwise is probably BS-ing you. Give it a thought - do you expect to work at the same workplace for 7 years or are you more likely to get bored?</li>
<li><a href="https://github.com/holman/extended-exercise-windows">Check companies offering that</a>.</li>
</ul>
<h4 id="special-mention-perquisite-tax-on-esops-in-india">Special mention: Perquisite tax on ESOPs in India</h4>
<ul>
<li>The &ldquo;Perquisite Tax&rdquo; means that if you are exercising (buying) shares at a lower than market value (FMV), then you have to pay tax on the difference (the notional profit).</li>
<li>This government counts this as profit you have made because you purchased an asset of higher value for a cheaper price. The absolutely bonkers fact: This only applies to ESOPs.</li>
</ul>
<h4 id="special-mention-esops-as-golden-hand-cuffs">Special mention: ESOPs as golden hand-cuffs</h4>
<ul>
<li>ESOPs are called &ldquo;golden handcuffs&rdquo; for a reason. If you cannot afford to exercise the shares you have vested, the fear of not missing out on the potential profit would prevent you from quitting.</li>
<li>Being able to afford to exercise large ESOPs with uncertain liquidity is a luxury that employees cannot afford in certain geographies. Especially in India, there is a &ldquo;Perquisite Tax&rdquo; to be paid on the ESOPs during exercise.</li>
</ul>
<blockquote>
<p>Checkout <a href="https://twitter.com/HashNuke/status/1375134640143360002">this Twitter thread</a></p>
</blockquote>
<h4 id="special-mention-on-esop-buybacks">Special mention on ESOP buybacks</h4>
<p><a href="https://twitter.com/Nithin0dha/status/1464590485582794756">Nithin Kamath&rsquo;s tweet</a> - read his entire Twitter thread.</p>
<h2 id="other-details">Other details</h2>
<h4 id="does-the-company-have-an-employee-stock-purchase-plan-applies-to-public-companies-only">Does the company have an Employee Stock Purchase Plan? (applies to public companies only)</h4>
<ul>
<li>What are the details of the ESPP?</li>
<li>If the employer is a public company, they can help you purchase listed shares at a discounted price.</li>
</ul>
<h4 id="how-many-leaves-do-employees-get-per-year">How many leaves do employees get per year?</h4>
<ul>
<li>Are employees allowed to carry-forward unused leaves?</li>
<li>Unlimited leaves are BS. Get an absolute number that employees are allowed per year.</li>
<li>Check for maternity &amp; paternity leaves. You may not be expanding your family. But this is an indicator of compassion.</li>
</ul>
<h4 id="does-the-company-work-remote-how-does-the-team-collaborate">Does the company work remote? How does the team collaborate?</h4>
<ul>
<li>If remote work is what you want, check about it.</li>
<li>What are the expected work hours? If you are uncomfortable with frequent meetings outside of your preferred/usual work hours, check about that too.</li>
<li>Choosing a timezone for a globally distributed organization is acceptable. But internal meetings being setup always/frequently in a particular timezone is being biased and is a red-flag indicating not respecting personal time.</li>
</ul>
<h4 id="is-the-company-providing-you-with-necessary-tools">Is the company providing you with necessary tools?</h4>
<ul>
<li>If you have to go through a week of approvals &amp; discussion to buy a $29 tool for your team, it means the company isn&rsquo;t seeing value in what you are doing.</li>
</ul>
<h2 id="finale">Finale</h2>
<h4 id="what-if-you-join-the-company-and-find-out-that-the-company-lied-about-certain-details">What if you join the company and find out that the company lied about certain details?</h4>
<p><a href="https://thedecisionlab.com/biases/the-sunk-cost-fallacy/">Sunk cost</a>. Find a new workplace immediately.</p>
<h4 id="what-if-the-potential-employer-is-not-willing-to-reveal-details">What if the potential employer is not willing to reveal details?</h4>
<ul>
<li>They&rsquo;ll find another engineer. You find another workplace.</li>
<li>You are going to spend a significant part of your time/life working at this place. If you do not have enough details to judge the workplace, skip the workplace. This will save you some ache in the long run.</li>
</ul>
<h4 id="i-spent-time-a-few-years-at-a-workplace-without-these-details-what-to-do">I spent time a few years at a workplace without these details. What to do?</h4>
<ul>
<li>Talk to someone at your workplace and ask for these details.</li>
<li>If details are still unclear after talking to an appropriate person, then probably consider it <a href="https://thedecisionlab.com/biases/the-sunk-cost-fallacy/">sunk cost</a>, so find a new workplace.</li>
</ul>
]]></content:encoded></item><item><title>Notes on the tar command</title><link>https://www.define.run/posts/tar/</link><pubDate>Thu, 23 Sep 2021 00:35:18 +0530</pubDate><guid>https://www.define.run/posts/tar/</guid><description> Because I cannot find the best documentation everytime I need to remember tar command.</description><content:encoded><![CDATA[ <blockquote>
<p>These notes were taken when making some changes for <a href="https://github.com/cncf/cnf-testsuite">cncf/cnf-testsuite</a>. I had to understand the options being used with the tar command before making the changes.</p>
</blockquote>
<p>These commands work fine on linux. But if on Mac, install <code>gnu-tar</code> with brew to get these commands working as expected.</p>
<h2 id="extract-and-compress">Extract and compress</h2>
<pre tabindex="0"><code># hello is a directory
$ ls .
hello tryouts somedir

# Compress
$ tar czvf hello.tar.gz hello

# Change to a directory to experiment
$ cd tryouts &amp;&amp; mv ../hello.tar.gz .

# Extract
$ tar zxvf hello.tar.gz
$ ls
hello

# Cleanup
$ cd .. &amp;&amp; rm -rf tryouts
</code></pre><h2 id="change-directory-when-compressing">Change directory when compressing</h2>
<pre tabindex="0"><code># hello has two directories
$ ls hello
world greeting

# Compress, with change directory option
$ tar czvf hello.tar.gz -C hello .

# Change to a directory to experiment
$ cd tryouts &amp;&amp; mv ../hello.tar.gz .

# Extract
$ tar zxvf hello.tar.gz
$ ls
hello.tar.gz world greeting

# Notice magic? No parent directory when extracting
</code></pre><h2 id="compress-only-select-directoriesfiles">Compress only select directories/files</h2>
<pre tabindex="0"><code># hello has two directories
$ ls hello
world greeting

# Compress, with change directory option and select world dir only
$ tar czvf hello.tar.gz -C hello world

# Change to a directory to experiment
$ cd tryouts &amp;&amp; mv ../hello.tar.gz .

# Extract
$ tar zxvf hello.tar.gz
$ ls
hello.tar.gz world

# See? No greeting dir because we did not compress it
</code></pre>]]></content:encoded></item><item><title>Dev notes: aws-cdk not updating Lambda function</title><link>https://www.define.run/posts/cdk-not-updating-lambda/</link><pubDate>Tue, 21 Sep 2021 09:30:01 +0530</pubDate><guid>https://www.define.run/posts/cdk-not-updating-lambda/</guid><description> This post contains notes about issues faced and solutions, when attempting to update a Lambda function deployed using aws-cdk.</description><content:encoded><![CDATA[ <p>So you created a Lambda function via aws-cdk. The first deploy worked fine and you were able to try the function. When attempting to deploy an update of the function, no changes are deployed. The output of the <code>cdk diff</code> command says &ldquo;There were no differences&rdquo;.</p>
<p>When using the aws-cdk Lambda docs and examples, the code for the Lambda function is fetched by <code>lambda.Code.fromAsset()</code> This is from a local directory within the cdk project and works as expected because the cdk is able to calculate a hash of the files to detect a change in the code.</p>
<p>My GitHub Actions workflow was pushing zip files to S3 containing the code for the Lambda functions. This meant that I use <code>lambda.Code.fromBucket</code>.</p>
<p>Something like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// bucket is of type Bucket construct from aws-cdk/aws-s3 package.
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// This is path of the release zip file in s3.
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">releaseFileKey</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;my-function/latest.zip&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">fn</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">lambda</span>.Function(<span style="color:#66d9ef">this</span>, <span style="color:#e6db74">&#39;MyFunction&#39;</span>, {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">runtime</span>: <span style="color:#66d9ef">lambda.Runtime.NODEJS_14_X</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">handler</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;handler.hello&#39;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">code</span>: <span style="color:#66d9ef">lambda.Code.fromBucket</span>(<span style="color:#a6e22e">bucket</span>, <span style="color:#a6e22e">releaseFileKey</span>)
</span></span><span style="display:flex;"><span>})
</span></span></code></pre></div><p>cdk would not detect updates to the lambda function. There are some notes in the cdk documentation that mentions passing the version options to inform cdk that an update is being made.</p>
<p>I was looking for a mechanism to be able to pass the version of the lambda function to deploy, to the <code>cdk deploy</code> command. I definitely do not want to be updating the s3 release file name in the stack definitions manually everytime I want to deploy.</p>
<p>I stumbled upon <code>CfnParameters</code>. This can be used to pass parameters to the cdk on the command line.</p>
<p>I made some code changes to read <code>releaseFileKey</code> using <code>CfnParameter</code>. Here is how I would pass the release file key in the command line.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>$ cdk deploy MyAppStack <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>    --parameter MyAppStack:releaseFileKey<span style="color:#f92672">=</span>my-function/3badf1b7.zip
</span></span></code></pre></div><p>Nope - that did not work. The Lambda function still wasn&rsquo;t being updated when I passed a new file key that included the git commit SHA in the file name. When I opened the CloudFormation templates in <code>cdk.out</code> dir, I noticed that the places where I was expected to see the release file key, there was something like <code>{&quot;ref&quot;: &quot;releaseFileKey&quot;}</code>.</p>
<p>I learnt that these CfnParameters are CloudFormation parameters that are injected during deploy time. The CloudFormation templates generated by cdk would always have placeholders for these parameters and therefore the cdk would not detect any changes to deploy.</p>
<h2 id="solutions-that-worked">Solutions that worked</h2>
<h3 id="option-1-read-the-s3-file-key-from-environment-variables">Option-1: Read the S3 file key from environment variables</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">fn</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">lambda</span>.Function(<span style="color:#66d9ef">this</span>, <span style="color:#e6db74">&#39;MyFunction&#39;</span>, {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">runtime</span>: <span style="color:#66d9ef">lambda.Runtime.NODEJS_14_X</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">handler</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;handler.hello&#39;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">code</span>: <span style="color:#66d9ef">lambda.Code.fromBucket</span>(<span style="color:#a6e22e">bucket</span>, <span style="color:#a6e22e">process</span>.<span style="color:#a6e22e">env</span>.<span style="color:#a6e22e">RELEASE_FILE</span>)
</span></span><span style="display:flex;"><span>})
</span></span></code></pre></div><p>Run the diff command now and you&rsquo;ll notice cdk is detecting changes to the stack and will update the Lambda function.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>RELEASE_FILE<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;my-function/3badf1b7.zip&#34;</span> cdk diff --all
</span></span></code></pre></div><h3 id="option-2-add-the-code-version-as-an-env-var-that-is-passed-to-the-lambda-function">Option-2: Add the code version as an env var that is passed to the Lambda function</h3>
<p>Incase your S3 file name always remains the same, then the other option is to pass some environment variable to your Lambda function that always changes when your code changes.</p>
<p>The easy way is to pass the git commit SHA like below.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">fn</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">lambda</span>.Function(<span style="color:#66d9ef">this</span>, <span style="color:#e6db74">&#39;MyFunction&#39;</span>, {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">runtime</span>: <span style="color:#66d9ef">lambda.Runtime.NODEJS_14_X</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">handler</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;handler.hello&#39;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">code</span>: <span style="color:#66d9ef">lambda.Code.fromBucket</span>(<span style="color:#a6e22e">bucket</span>, <span style="color:#e6db74">&#34;my-function/latest.zip&#34;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">environment</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">GIT_VERSION</span>: <span style="color:#66d9ef">process.env.RELEASE_VERSION</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>})
</span></span></code></pre></div><p>And then pass the env var to the diff or deploy commands like this.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>RELEASE_VERSION<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;3badf1b7&#34;</span> cdk diff --all
</span></span></code></pre></div><h3 id="option-3-pass-version-options-to-lambdafunction">Option-3: Pass version options to lambda.Function</h3>
<p>You can also pass the version options to <code>lambda.Function</code> like below. But it is required that you access the <code>currentVersion</code> property after the lambda function is declared. This ensures that the version is output to the CloudFormation template. Notice the last line in the snippet below.</p>
<p>If you notice below, the snippet still reads the release version from an env var.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">fn</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">lambda</span>.Function(<span style="color:#66d9ef">this</span>, <span style="color:#e6db74">&#39;MyFunction&#39;</span>, {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">runtime</span>: <span style="color:#66d9ef">lambda.Runtime.NODEJS_14_X</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">handler</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;handler.hello&#39;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">code</span>: <span style="color:#66d9ef">lambda.Code.fromBucket</span>(<span style="color:#a6e22e">bucket</span>, <span style="color:#e6db74">&#34;my-function/latest.zip&#34;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">currentVersionOptions</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">codeSha256</span>: <span style="color:#66d9ef">process.env.RELEASE_VERSION</span>
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>})
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">fn</span>.<span style="color:#a6e22e">currentVersion</span>
</span></span></code></pre></div><p>Try running the cdk diff or deploy commands with <code>RELEASE_VERSION</code> env var.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>RELEASE_VERSION<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;3badf1b7&#34;</span> cdk diff --all
</span></span></code></pre></div>]]></content:encoded></item><item><title>Building a custom QMK firmware</title><link>https://www.define.run/posts/building-qmk-firmware/</link><pubDate>Thu, 15 Jul 2021 12:20:13 +0530</pubDate><guid>https://www.define.run/posts/building-qmk-firmware/</guid><description> Notes on enabling AutoShift and TapDance for the Drop Preonic keyboard</description><content:encoded><![CDATA[ <p>I&rsquo;ve been used to Tap Dance and AutoShift on the Planck EZ and I wanted the same on my Preonic. This led me to find out that <a href="https://config.qmk.fm/">QMK Configurator</a> does not support the features out of the box. Downloading the QMK firmware and customising it is the only way.</p>
<h2 id="first-step-install-qmk">First step: Install qmk</h2>
<p>For Mac OS, using homebrew for  installation.</p>
<pre tabindex="0"><code>brew install qmk
</code></pre><p>Install some dependencies using <a href="https://github.com/samhocevar-forks/qmk-firmware/blob/master/docs/getting_started_build_tools.md">this doc as a reference</a></p>
<p>Setup QMK using the below command. This will download the QMK firmware for you and if required install any dependencies. On my Mac and this took about 5min.</p>
<pre tabindex="0"><code>qmk setup
</code></pre><p>The output should look like this - along with some git clone to clone the repo. (This is the output when qmk setup is run for the second time).</p>
<pre tabindex="0"><code>$ qmk setup
Ψ Found qmk_firmware at /Users/hashnuke/qmk_firmware.
Ψ QMK Doctor is checking your environment.
Ψ CLI version: 0.2.0
Ψ QMK home: /Users/hashnuke/qmk_firmware
Ψ Detected macOS 11.3.1.
Ψ All dependencies are installed.
Ψ Found arm-none-eabi-gcc version 9.3.1
Ψ Found avr-gcc version 8.4.0
Ψ Found avrdude version 6.3
Ψ Found dfu-util version 0.10
Ψ Found dfu-programmer version 0.7.2
Ψ Submodules are up to date.
Ψ QMK is ready to go
</code></pre><h2 id="create-new-keymap">Create new keymap</h2>
<p>My keyboard layout is going to be based on the default Preonic rev3 layout. I identified this name from the list of keyboards from <a href="https://config.qmk.fm/">QMK configurator</a></p>
<pre tabindex="0"><code>qmk new-keymap -kb preonic/rev3
</code></pre><p>The output should look like below:</p>
<pre tabindex="0"><code>$ qmk new-keymap -kb preonic/rev3
Keymap Name: hn-preonic
Ψ hn-preonic keymap directory created in: /Users/hashnuke/qmk_firmware/keyboards/preonic/keymaps/hn-preonic
Ψ Compile a firmware with your new keymap by typing:

  qmk compile -kb preonic/rev3 -km hn-preonic
</code></pre><p>Within the directory mentioned above in the output, I made the following changes.</p>
<h2 id="enable-tap-dance">Enable Tap Dance</h2>
<p><strong>Enable Tap Dance in rules.mk file</strong></p>
<pre tabindex="0"><code>TAP_DANCE_ENABLE = yes
</code></pre><p><strong>Define tapping interaval in config.h file</strong></p>
<pre tabindex="0"><code>#define TAPPING_TERM 175
</code></pre><p><strong>Define tap dance functionality in keymap.c</strong></p>
<p>Add this snippet right below the include statements. What I&rsquo;ve done is make the escape key behave like control if double-tapped.</p>
<pre tabindex="0"><code>// Tap Dance declarations
enum {
    TD_ESC_CAPS,
};

// Tap Dance definitions
qk_tap_dance_action_t tap_dance_actions[] = {
    // Tap once for Escape, twice for Caps Lock
    [TD_ESC_CAPS] = ACTION_TAP_DANCE_DOUBLE(KC_ESC, KC_LCTL),
};
</code></pre><p>Then in the same file, replace any occurrence of <code>KC_ESC</code> with <code>TD(TD_ESC_CAPS)</code></p>
<h2 id="enable-autoshift">Enable AutoShift</h2>
<p>Add the below snippet to rules.mk</p>
<pre tabindex="0"><code>AUTO_SHIFT_ENABLE = yes
</code></pre><p>Add the below snippet to config.h</p>
<pre tabindex="0"><code>#define AUTO_SHIFT_TIMEOUT 200
</code></pre><h2 id="compile-the-firmware-and-flash-your-keyboard-with-it">Compile the firmware and flash your keyboard with it</h2>
<pre tabindex="0"><code>$ qmk flash -km hn-preonic -kb preonic/rev3
</code></pre><p>Once this command begins, ensure to reset your keyboard for the bootloader to be available. On the Drop Preonic, this is at the bottom of the keyboard - you&rsquo;ll need a needle or something thin to press it.</p>
<p>The build would then be available in the <code>~/qmk_firmware/.build</code> directory. The bin file will be flashed to your keyboard.</p>
<h2 id="references">References</h2>
<ul>
<li><a href="https://beta.docs.qmk.fm/using-qmk/software-features/feature_tap_dance">QMK docs - tap-dance</a></li>
<li><a href="https://beta.docs.qmk.fm/using-qmk/software-features/feature_auto_shift">QMK docs - auto-shift</a></li>
</ul>
]]></content:encoded></item><item><title>Hire when there is enough work for a year</title><link>https://www.define.run/hire-when/</link><pubDate>Sun, 06 Jun 2021 17:30:20 +0530</pubDate><guid>https://www.define.run/hire-when/</guid><description> Notes on when to hire new team members.</description><content:encoded><![CDATA[ <p><em>(Originally authored on 20 July 2020 in my private notes. These are my notes on when to hire new team members.)</em></p>
<p>We may see some pending work often and identify that as the responsibility of people with a certain title or specialization. If this work comes up enough times - it is very likely that there would be a proposal to hire for this role.</p>
<p>Here&rsquo;s how it usually goes: A company needs a mobile app built as a companion to their primary product line.</p>
<p>None of the current engineers can build the mobile app due to various reasons. The reasons may include the lower priority of the mobile app in comparison to the product backlog, missing skill/experience on the team, or urgency in the organization to ship the mobile app.</p>
<p>So the hiring starts. Position is opened and interviews happen. A suitable person is hired to build mobile apps and a manager is assigned to them.</p>
<h2 id="the-managers-pain">The manager&rsquo;s pain</h2>
<p>A few months later, the mobile app is built and released. Congratulations to the team - they have delivered good work. The mobile app has no bugs. They require very minimal maintenance work. The organization now prefers to focus on other priority items and no more updates are planned to the mobile app.</p>
<p>The manager and the mobile engineer begin experimenting with a few other ideas, but quickly realise that any further work on the mobile apps require other teams to build/expose APIs. And these teams would not do it - because it is not their priority at the moment or for the next quarter.</p>
<p>Days or weeks later - soon enough, the manager begins to find it difficult to create work for the mobile engineer. The eventual end is to let go of this mobile engineer.</p>
<p><em>Hard truth: The person hired to build mobile apps should not have been hired.</em></p>
<h2 id="the-roadmap-check-for-hiring-full-time">The roadmap check for hiring full-time</h2>
<p>An easy way to avoid the above scenario from happening is to check if there&rsquo;s enough work for a full year before hiring.</p>
<ul>
<li>Try creating a roadmap of work to be done by this future person. List work to be done without which the company cannot run.</li>
<li>Most importantly, for anyone hired in new position to succeed, list other teams/people who should be supporting them. Ensure to check their priority list or roadmap for the next one year.</li>
</ul>
<p>If there is not enough work for a year, but if it still requires another person on your team, then hiring a part-time position or a contractor would be better.</p>
]]></content:encoded></item><item><title>Bring clarity, not blockers</title><link>https://www.define.run/bring-clarity-not-blockers/</link><pubDate>Tue, 01 Jun 2021 12:21:06 +0530</pubDate><guid>https://www.define.run/bring-clarity-not-blockers/</guid><description> Notes on participating in conversations as a topic expert.</description><content:encoded><![CDATA[ <p><em>(Originally written on 29 July 2020 in my personal notes)</em></p>
<p>Ever been in a situation like below?</p>
<p>A Manager, stakeholder or old employee with historical context walks into a meeting only to talk about an issue, but add nothing else. As a next step, the team tries to solve the problem, but the solutions are either not reviewed, not accepted, or worse - the same stakeholders complain about it after it has been released to production. If this happens often, then more should be expected of this topic expert.</p>
<p>Topic experts at workplaces would very often take part in discussions to provide perspective. This includes throwing light on details and scenarios that may be unknown to the team.</p>
<p>It is equally important to make an attempt to help resolve the issue. So if you take part in a conversation, do it with the intention of bringing clarity, not blockers.</p>
<h2 id="start-with-heres-something-that-we-should-be-aware-of">Start with <em>&ldquo;Here&rsquo;s something that we should be aware of&rdquo;</em></h2>
<p>Don&rsquo;t start by shooting someone in the room by with <em>&ldquo;Your solution does not work&rdquo;</em>. Instead, use facts and reasoning to make the team realize that the solution might not work. After all, even the topic expert can be wrong.</p>
<h2 id="continue-with-heres-something-we-can-do-to-resolve-this">Continue with <em>&ldquo;Here&rsquo;s something we can do to resolve this&rdquo;</em></h2>
<p>It is of utmost importance that you attempt to provide a solution or atleast parts of it. By doing so you admit that you want to resolve it.</p>
<blockquote>
<p>My self-observation is that I communicate clearly when I write. So I prefer to write a wiki page or send emails, instead of verbal conversations. YMMV.</p>
</blockquote>
<h2 id="and-extend-it-with-heres-what-i-can-do-to-help">And extend it with <em>&ldquo;Here&rsquo;s what I can do to help&rdquo;</em></h2>
<p>By providing perspective and a possible solution, you already contribute towards moving forward. But if there&rsquo;s something you can specifically do, say it out loud. It matters that you are willing to be part of the solution.</p>
<p>When you contribute to the solution, your role changes from a mere stakeholder, to someone that is part of the solution - skin in the game. This helps in environments where stakeholders are not trusted to stick to decisions.</p>
]]></content:encoded></item><item><title>React hooks: useEffect and useCallback</title><link>https://www.define.run/posts/useeffect-vs-usecallback/</link><pubDate>Thu, 13 May 2021 00:00:00 +0000</pubDate><guid>https://www.define.run/posts/useeffect-vs-usecallback/</guid><description> &lt;p>These are my notes on using React hooks.&lt;/p></description><content:encoded><![CDATA[ <p>These are my notes on using React hooks.</p>
<h3 id="useeffect">useEffect</h3>
<ul>
<li>Use to perform anything that has side-effects (API calls, modify local storage, etc)</li>
<li>This does not block visual renders</li>
</ul>
<h3 id="usecallback">useCallback</h3>
<p>Use to create a memoized function that doesn&rsquo;t have to change during renders - unless one of the dependencies have changed.</p>
<h3 id="to-run-something-that-has-a-side-effect">To run something that has a side-effect</h3>
<ul>
<li>Use <code>useCallback</code> to create a memoized function</li>
<li>Then <code>useEffect</code> over the memoized function to mark it as as having side-effects</li>
</ul>]]></content:encoded></item><item><title>Postgres: ORDER BY custom text order</title><link>https://www.define.run/posts/postgres-custom-order/</link><pubDate>Mon, 19 Apr 2021 00:00:00 +0000</pubDate><guid>https://www.define.run/posts/postgres-custom-order/</guid><description> &lt;p>Found this scenario where I wanted to (out of pure laziness) sort records by specific order of a column&amp;rsquo;s possible values. I looked around and to my surprise I found a way to do it.&lt;/p></description><content:encoded><![CDATA[ <p>Found this scenario where I wanted to (out of pure laziness) sort records by specific order of a column&rsquo;s possible values. I looked around and to my surprise I found a way to do it.</p>
<ul>
<li>I would like to list the in-stock products first.</li>
<li>I have a products table with an <code>availability</code> column.</li>
<li>The valid values for <code>availability</code> column are &lsquo;instock&rsquo; and &lsquo;outofstock&rsquo;.</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">SELECT</span> <span style="color:#f92672">*</span> products
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">ORDER</span> <span style="color:#66d9ef">BY</span> array_position(
</span></span><span style="display:flex;"><span>  array[<span style="color:#e6db74">&#39;instock&#39;</span>,<span style="color:#e6db74">&#39;outofstock&#39;</span>],
</span></span><span style="display:flex;"><span>  products.availability
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>The above SQL uses the <code>array_position</code> function. It requires two arguments.</p>
<ul>
<li>A list of sorted values</li>
<li>The column that we want to sort.</li>
</ul>
<pre tabindex="0"><code>array_position(sorted_values, column_name)
</code></pre><p>If you are using #RubyOnRails, that would be the following:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-ruby" data-lang="ruby"><span style="display:flex;"><span><span style="color:#75715e"># First turn your custom SQL into an Arel SQL literal</span>
</span></span><span style="display:flex;"><span>order_sql <span style="color:#f92672">=</span> <span style="color:#66d9ef">Arel</span><span style="color:#f92672">.</span>sql(<span style="color:#e6db74">&#34;array_position(array[&#39;instock&#39;,&#39;outofstock&#39;], products.availability)&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Pass the Arel literal to the order method</span>
</span></span><span style="display:flex;"><span>products <span style="color:#f92672">=</span> <span style="color:#66d9ef">Product</span><span style="color:#f92672">.</span>order(order_sql)
</span></span></code></pre></div><h3 id="what-if-the-column-has-null-values">What if the column has <code>NULL</code> values?</h3>
<p>What I have is scraped data. I found out that there are times when the <code>availability</code> is <code>NULL</code>.</p>
<p>If I just wanted NULL values first or last, I would use <code>NULLS FIRST</code> or <code>NULLS LAST</code>. The SQL would be something like below.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">SELECT</span> <span style="color:#f92672">*</span> products
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">ORDER</span> <span style="color:#66d9ef">BY</span> array_position(array[<span style="color:#e6db74">&#39;instock&#39;</span>,<span style="color:#e6db74">&#39;outofstock&#39;</span>], products.availability) NULLS <span style="color:#66d9ef">FIRST</span>
</span></span></code></pre></div><p>But for products whose availability I am not sure of (<code>NULL</code> value), I want them to be listed on top of products that are out of stock. So I now include the NULL value as the list of values to order by.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">SELECT</span> <span style="color:#f92672">*</span> products
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">ORDER</span> <span style="color:#66d9ef">BY</span> array_position(array[<span style="color:#e6db74">&#39;instock&#39;</span>,<span style="color:#66d9ef">NULL</span>,<span style="color:#e6db74">&#39;outofstock&#39;</span>], products.availability)
</span></span></code></pre></div><h3 id="an-easier-way">An easier way</h3>
<p>If the values for a column are going to a finite list of values, this column would benefit from storing the availability as integers, along with using <code>NULLS LAST</code> or <code>NULLS FIRST</code>.</p>
<blockquote>
<p>I&rsquo;ve been toying with #Postgres for a hobby project and I&rsquo;m learning quite a lot of nice things. If this is something you are interested in - follow me on <a href="https://twitter.com/HashNuke">@HashNuke</a></p>
</blockquote>]]></content:encoded></item><item><title>Notes on building mechanical keyboards</title><link>https://www.define.run/posts/keyboards/</link><pubDate>Sun, 13 Sep 2020 00:00:00 +0000</pubDate><guid>https://www.define.run/posts/keyboards/</guid><description> &lt;p>The keyboard in the picture is my Planck EZ. I explored about building keyboards (and will hopefully do this someday).&lt;/p>
&lt;p>~ This is a rabbit hole ~&lt;/p></description><content:encoded><![CDATA[ <p>The keyboard in the picture is my Planck EZ. I explored about building keyboards (and will hopefully do this someday).</p>
<p>~ This is a rabbit hole ~</p>
<h2 id="places-that-sell-stuff">Places that sell stuff</h2>
<ul>
<li><a href="https://kbdfans.com/">KBDFans</a></li>
<li><a href="https://techkeys.us/">TechKeys</a> for keycaps and some weird goodies like 1-key keyboards</li>
<li><a href="https://spacecat.design/">SpaceCat</a> - apart from other things has an <a href="https://spacecat.design/collections/pcbs-cases-kits/products/alpha28-pcbs">Alpha PCB</a> to build a sample keyboard. Might be better to get a Gherkin from MechBoards.</li>
<li><a href="https://mechboards.co.uk/product-category/kits/">MechBoards</a> has some good kits. The Iris kit and Lets Split kit are good. Compare the cost when buying.</li>
<li><a href="https://keeb.io">keeb.io</a> has kits and guides</li>
<li><a href="https://www.1upkeyboards.com/">1upkeyboards</a></li>
<li><a href="https://pimpmykeyboard.com/all-products/keycaps/keysets/">PimpMyKeyboard</a></li>
<li><a href="https://kprepublic.com/">kprepublic.com</a></li>
<li><a href="https://sentraq.com/collections/all">Sentraq</a></li>
<li><a href="https://cannonkeys.com/">CannonKeys</a> sells some inexpensive practice kits</li>
<li><a href="https://keyhive.xyz/">KeyHive</a> has a <a href="https://keyhive.xyz/shop/may-pad">$18 Maypad practice kit</a></li>
<li>Happy Hacking Keyboard (HHKB) and Leopold F660C
<ul>
<li>Both are expensive.</li>
<li>Both use Torpe switches.</li>
</ul>
</li>
<li><a href="https://geekhack.org/index.php?topic=45672.0">GeekHack has a list of keycap sellers</a></li>
<li><a href="https://candykeys.com/category:switches">CandyKeys</a> has a lot of keycaps and switch options</li>
<li><a href="https://www.jellykey.com/">JellyKey</a></li>
<li><a href="https://x-bows.com/">X-bows</a> keyboards</li>
<li><a href="https://mechanicalkeyboards.com/">MechanicalKeyboards</a></li>
<li>Check AliExpress and NewEgg too</li>
</ul>
<h2 id="notes-for-custom-builds">Notes for custom builds</h2>
<h4 id="case--pcbs">Case &amp; PCBs</h4>
<ul>
<li>Check if the PCB has LEDs or if it atleast supports it. SMB LEDs are the kinds of LEDs that I&rsquo;ve come across so far - flat and can be soldered to PCB.</li>
<li>Get a Tofu65 case with a KBD67 PCB.</li>
</ul>
<h4 id="key-switches">Key switches</h4>
<ul>
<li>Gateron can be better than Cherry MX.</li>
<li>Browns are better for me.</li>
<li>Checkout low-profile switches (comes in both optical and mechanical variants).</li>
<li>Sometime tryout Holy Panda or Glorious Panda switches.</li>
</ul>
<h4 id="keycaps">Keycaps</h4>
<ul>
<li>Keycaps come in various sizes. 1u and 2u are common. Check size before buying.</li>
<li>Check if the keycaps work with the key switch being used.</li>
</ul>
<h4 id="controller">Controller</h4>
<ul>
<li>Use an Elite-C controller which is a clone of SparkFun Pro Micro, but with a USB-C instead of micro-USB. Pro Micro is cheaper but the usb port has a reputation of getting ripped off the board.</li>
<li>QMK firmware <a href="https://qmk.fm/keyboards/">supports a number of keyboards</a></li>
<li>QMK supports Atmel and AVR ICs. Infact the Pro Micro is an Arduino clone. Maybe I can use one of the Arduino boards? (QMK seems to support ATMEGA328up)</li>
<li>What if I used my own controller?
<ul>
<li>There is a possibility of using a Teensy microcontroller. But again it&rsquo;s micro-USB.</li>
<li><a href="https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html">SeeedStudio Xiao</a> is cute (like a <a href="https://tomu.im/">Tomu</a> with pin-outs). But the cortex-based chip used by the Xiao is not supported by QMK.</li>
</ul>
</li>
</ul>
<h4 id="kits">Kits</h4>
<ul>
<li>Check if case and keycaps are being sold with the kits. Else have to buy them.</li>
</ul>
<h2 id="other-peoples-notes-on-making-keyboards">Other people&rsquo;s notes on making keyboards</h2>
<ul>
<li><a href="http://40percent.club/">40percent.club</a> is a blog about building keyboards</li>
<li><a href="https://www.dribin.org/dave/keyboard/one_html/">Building a keyboard matrix</a></li>
<li><a href="https://nematicslab.com/">NematicsLab</a> has some electronics tutorials</li>
<li><a href="https://gitlab.com/technomancy/atreus">technomancy&rsquo;s notes on building atreus</a></li>
</ul>]]></content:encoded></item><item><title>Reading list</title><link>https://www.define.run/reading-list/</link><pubDate>Sat, 05 Sep 2020 00:00:00 +0000</pubDate><guid>https://www.define.run/reading-list/</guid><description> These are a list of books I want to read the next 5yrs</description><content:encoded><![CDATA[ <h3 id="design-art--inspiration">Design, Art &amp; Inspiration</h3>
<ul>
<li>The Non-designer&rsquo;s Design Book</li>
<li>Refactoring UI</li>
<li>How to draw almost everything</li>
<li>How to draw almost everyday</li>
<li>Drawing on the right side of the brain</li>
<li>A Profound Waste of Time</li>
<li>Designing Games</li>
<li>Creativity Inc</li>
<li>Zen Pencils</li>
<li>&ldquo;How Buildings Learn&rdquo; by Stewart Brand</li>
</ul>
<h3 id="about-building-products-and-running-companies">About building products and running companies</h3>
<ul>
<li>Traction (by Gabriel Weinberg and Justin Mares)</li>
<li>The Mom Test</li>
<li>Obviously Awesome</li>
<li>Company of One</li>
<li>The Startup Owner&rsquo;s Manual</li>
<li>Fanatical Prospecting</li>
<li>Zero to One</li>
</ul>
<h3 id="people-biases-and-such">People, Biases and such</h3>
<ul>
<li>The Art of Thinking Clearly</li>
<li>Crucial Conversations</li>
<li>Super Thinking</li>
<li>Never split the difference</li>
</ul>
<h3 id="market-economy-systems-and-such">Market, Economy, Systems and such</h3>
<ul>
<li>Good economics for hard times</li>
<li>A Little History of Economics</li>
<li>Thinking in Systems</li>
<li>Black Swan</li>
<li>The Intelligent Investor</li>
<li>The Rise and Fall of Nations</li>
<li>Skin in the game</li>
<li>Anti-Fragile</li>
</ul>
<h3 id="upskill">Upskill</h3>
<ul>
<li>Deep Learning for Coders with fastai and PyTorch</li>
<li>A Programmer&rsquo;s Introduction to Mathematics</li>
<li><a href="https://andrewnc.github.io/blog/everyday_data_science.html">Everyday Data Science</a> by Andrew Carr</li>
</ul>
<h3 id="about-stuff-around-me">About stuff around me</h3>
<ul>
<li>Stuff Matters</li>
<li>Surely you&rsquo;re joking Mr.Feynman</li>
<li>Feynman Lectures on Physics: Volume 1</li>
<li>Feynman Lectures on Physics: Volume 2</li>
<li>Feynman Lectures on Physics: Volume 3</li>
</ul>
<h3 id="copywriting">Copywriting</h3>
<ul>
<li>Breakthrough Advertising</li>
<li>The Elements of Style</li>
</ul>
<h3 id="fiction">Fiction</h3>
<ul>
<li>The Hitchhiker&rsquo;s guide to the galaxy</li>
<li>Eleanor Oliphant is Completely Fine</li>
</ul>
]]></content:encoded></item><item><title>Traction</title><link>https://www.define.run/traction-book/</link><pubDate>Sun, 16 Aug 2020 00:00:00 +0000</pubDate><guid>https://www.define.run/traction-book/</guid><description><![CDATA[ <p><em>&ndash; This post is a work in progress. These are my notes from reading the <a href="https://l.define.run/traction">Traction book</a> &ndash;</em></p>]]></description><content:encoded><![CDATA[ <p><em>&ndash; This post is a work in progress. These are my notes from reading the <a href="https://l.define.run/traction">Traction book</a> &ndash;</em></p>
<h2 id="on-choosing-metrics-for-traction-channels">On choosing metrics for traction channels</h2>
<p><img src="https://www.define.run/images/posts/traction/channel-metrics.jpg" alt="Traction book: Channel metrics that matter"></p>
<h2 id="traction-channels">Traction channels</h2>
<p><img src="https://www.define.run/images/posts/traction/blogs.jpg" alt="Traction book: Targeting blogs and Publicity"></p>
<p><img src="https://www.define.run/images/posts/traction/publicity-and-sem.jpg" alt="Traction book: More on Publicity and Search Engine Marketing"></p>
<p><img src="https://www.define.run/images/posts/traction/sem-2.jpg" alt="Traction book: More on Search Engine Marketing"></p>
<p><img src="https://www.define.run/images/posts/traction/display-and-social-ads.jpg" alt="Traction book: Display Ads and Social Ads"></p>
<p><img src="https://www.define.run/images/posts/traction/offline-ads-and-seo.jpg" alt="Traction book: Offline Ads and SEO"></p>
<p><img src="https://www.define.run/images/posts/traction/content-and-community.jpg" alt="Traction book: Content Marketing and Community Building"></p>
<p><img src="https://www.define.run/images/posts/traction/email-marketing.jpg" alt="Traction book: Email Marketing"></p>
<p><img src="https://www.define.run/images/posts/traction/viral-marketing.jpg" alt="Traction book: Viral Marketing"></p>
<h2 id="reference-list-of-tractions-channels">Reference: List of tractions channels</h2>
<ul>
<li>[DONE] Targeting Blogs</li>
<li>[DONE] Publicity</li>
<li>[DONE] Unconventional PR</li>
<li>[DONE] Search Engine Marketing</li>
<li>[DONE] Social &amp; Display Ads</li>
<li>[DONE] Offline Ads</li>
<li>[DONE] Search Engine Optimization</li>
<li>[DONE] Content Marketing</li>
<li>[DONE] Email Marketing</li>
<li>[WIP] Engineering as Marketing</li>
<li>[DONE] Viral Marketing</li>
<li>[WIP] Business Development</li>
<li>Sales</li>
<li>Affiliate Programs</li>
<li>Existing Platforms</li>
<li>[WIP] Trade Shows</li>
<li>[WIP] Offline Events</li>
<li>Speaking Engagements</li>
<li>[DONE] Community Building</li>
</ul>]]></content:encoded></item><item><title>Notes on creating screencasts</title><link>https://www.define.run/posts/screencasting/</link><pubDate>Thu, 09 Jul 2020 00:00:00 +0000</pubDate><guid>https://www.define.run/posts/screencasting/</guid><description> These are some useful learnings when recording screencasts/videos.</description><content:encoded><![CDATA[ <h2 id="the-process-in-a-nutshell">The process in a nutshell</h2>
<ol>
<li>Write a script to follow.</li>
<li>Record the video first.</li>
<li>Then record the audio.</li>
<li>Add video and audio together.</li>
</ol>
<h2 id="recording">Recording</h2>
<p>Having a script before the recording helps keep the recording concise. You decide what goes in ahead of the recording and not during the recording. This is the secret to having byte-sized screencasts.</p>
<p>I want to focus on one thing and get that right. So I prefer recording audio and video separately.</p>
<p>You can record both at the same time as long as you get separate audio and video tracks that you can cut/edit later.</p>
<p><strong>It is OK to make mistakes in the same take/recording.</strong></p>
<p>In case of a mistakes or unexpected background noise during recordings, repeat the sentence or the action in the video. The mistakes can later be removed during editing. Retakes are not required.</p>
<h2 id="combining-audio-and-video">Combining audio and video</h2>
<p>The audio and video recordings might have unnecessary parts. Like &ldquo;huh&rdquo; in the audio or some unnecessary page load indicator. Having separate audio and video tracks allows you edit them separately and consolidate the length.</p>
<p><strong>Decide what your &ldquo;scenes&rdquo; are.</strong></p>
<p>Edit by considering &ldquo;scenes&rdquo; - parts of the video that have different contexts. In your video, the scenes might be editing different files or showing different screens of a web app.</p>
<h3 id="audio-longer-than-video">Audio longer than video</h3>
<p>If the audio for a particular scene is longer than the video, then consider doing one or more of the following:</p>
<ul>
<li>Add a &ldquo;Freeze frame&rdquo; for the video.</li>
<li>Space out your audio. A 2 second pause can be added during editing depending on how you want the video to feel.</li>
</ul>
<p>These help ensure that the screen/page being talked about remains in context while it is being talked about.</p>
<h3 id="video-longer-than-audio">Video longer than audio</h3>
<p>If the video for a particular scene is longer than the audio, then consider speeding up certain parts of the video for that scene.</p>
<p>Actions like typing form inputs, typing boilerplate code, page load, etc can be cut out or sped up (Video editing tools allow you to speed up select parts of the video).</p>
<blockquote>
<p>Code that is not the focus of the topic can be copy-pasted into the video as boilerplate. Just mention what the code does. <em>&ldquo;Lets copy-paste this code here. This helps with bla bla bla&hellip;&rdquo;</em></p>
</blockquote>
<h3 id="pace-the-audio-as-required">Pace the audio as required</h3>
<p>Use the pauses in the audio to decide what the user will perceive.</p>
<p>Want a breezy/fast screencast? Try having less pauses in the audio. Want a calm/slow screencast? Try having sufficient pauses in the audio.</p>
<h3 id="bonus-add-that-background-sound-">Bonus: Add that background sound 🎺🎸</h3>
<p>Pick some background music and add that as another audio track to your recording. Search for &ldquo;royalty free background sound&rdquo;.</p>
<p>For best results, try the following:</p>
<ul>
<li>Set the volume of this background sound track to 15%. You do not want that to be higher than the audio.</li>
<li>Add a &ldquo;Fade out&rdquo; effect to the background sound for the final 5 seconds of the recording.</li>
</ul>
<p>🎉 Done. You have your professional-quality screencast. 😃</p>
<h2 id="how-long-does-all-of-this-take">How long does all of this take?</h2>
<p>It usually takes me 2-3 hours to produce a 5min screencast. Most of this time goes into the initial prep and then later into listening/watching the output multiple times. Your pace might vary depending on what you are recording.</p>
<p>I have noticed that certain details that appear to me as quirks when editing, do not matter much in the final output. I do plan to practice not editing out certain things and leave them as is. This helps with avoiding the law of diminishing returns, but still keeps the screencast quality high.</p>
<hr>
<p><strong>&ndash; Some additional notes &ndash;</strong></p>
<p><em>[1] I also &ldquo;smooth&rdquo; the audio of my voice recording. Helps keep consistent volume levels even though I might have different pitches in the audio recording.</em></p>
<p><em>[2] I use <a href="https://www.telestream.net/screenflow/overview.htm">ScreenFlow</a> for all my recording work. But this process should work with other tools too.</em></p>
]]></content:encoded></item><item><title>Using Core Data Lab</title><link>https://www.define.run/posts/coredata-lab/</link><pubDate>Sun, 28 Jun 2020 00:00:00 +0000</pubDate><guid>https://www.define.run/posts/coredata-lab/</guid><description> &lt;p>Sometime ago I bought this app called &lt;a href="https://betamagic.nl/products/coredatalab.html">Core Data Lab&lt;/a>. The app helps inspect CoreData stores created by my mac apps. This is very handy when building Mac apps (probably iOS apps too).&lt;/p></description><content:encoded><![CDATA[ <p>Sometime ago I bought this app called <a href="https://betamagic.nl/products/coredatalab.html">Core Data Lab</a>. The app helps inspect CoreData stores created by my mac apps. This is very handy when building Mac apps (probably iOS apps too).</p>
<p>The entire walkthrough assumes that you are using Xcode to build your mac app.</p>
<h3 id="open-coredatalab">Open CoreDataLab</h3>
<p><img src="https://www.define.run/intro-screen.png" alt="CoreDataLab intro screen"></p>
<p>Click on &ldquo;Create a new Core Data Lab project&rdquo;.</p>
<h3 id="choose-your-app-file-the-one-you-are-building">Choose your app file (the one you are building)</h3>
<p>When creating a new CoreDataLab project, the app file has to be chosen first.</p>
<p><img src="https://www.define.run/select-app.png" alt="CoreDataLab - select app"></p>
<ul>
<li>Click on &ldquo;Select App&rdquo;. This opens the file picker window.</li>
<li>If you are using Xcode to build your mac app, then the use the keyboard shortcut <code>cmd+shift+G</code> to invoke a prompt to enter a custom folder path</li>
<li>Enter the path <code>~/Library/Developer/Xcode/DerivedData</code> and hit the enter key</li>
<li>Within this directory, look for your app&rsquo;s folder.</li>
</ul>
<p>If your app is called <code>Hello</code>, then the folder would look something like <code>Hello-23erwfaewra</code> (some random string follows the folder name).</p>
<p><img src="https://www.define.run/select-app-file.png" alt="CoreDataLab - select app file"></p>
<p>Within your app&rsquo;s directory, navigate into the following folder structure to find your <code>.app</code> file.</p>
<pre tabindex="0"><code>Build
-&gt; Projects
-&gt; Debug
-&gt; Hello.app
</code></pre><p><img src="https://www.define.run/select-app-file-path.png" alt="CoreDataLab - select app file path"></p>
<h3 id="after-selecting-the-app-continue-to-setup-the-database-path">After selecting the app, continue to setup the database path</h3>
<ul>
<li>Like how the app was selected, click on &ldquo;Select database&rdquo;</li>
<li>Use the <code>cmd+shift+G</code> shortcut to enter a custom path</li>
<li>If your app&rsquo;s identifier is <code>app.example.Hello</code>, then look for a directory like below:</li>
</ul>
<pre tabindex="0"><code>~/Library/Containers/app.example.Hello/
</code></pre><p>Once inside, navigate to the following path to find the SQLite database file (path depends on your app name).</p>
<pre tabindex="0"><code>Data
-&gt; Library
-&gt; Application Support
-&gt; Hello
-&gt; Hello.sqlite
</code></pre><p>The final path will look something like below. Click on the &ldquo;Continue&rdquo; button to proceed.</p>
<pre tabindex="0"><code>~/Library/Containers/app.example.Hello/Data/Library/Application Support/Hello/Hello.sqlite
</code></pre><h3 id="final-step-give-your-project-a-name">Final step: Give your project a name</h3>
<p>Once the project is named, that is the end of the setup.
Feel free to explore the data for your CoreData models.</p>
<p>Link - <a href="https://betamagic.nl/products/coredatalab.html">Core Data Lab website</a></p>]]></content:encoded></item><item><title>Making HTTP POST request in Swift using URLSession</title><link>https://www.define.run/posts/http-post-in-swift-using-urlsession/</link><pubDate>Tue, 16 Jul 2019 00:00:00 +0000</pubDate><guid>https://www.define.run/posts/http-post-in-swift-using-urlsession/</guid><description><![CDATA[ <blockquote>
<p>or <em>&ldquo;How to POST a form in Swift using URLSession&rdquo;</em></p>
</blockquote>
<p>These examples will be using <a href="http://httpbin.org">httpbin.org</a> to test our request.</p>]]></description><content:encoded><![CDATA[ <blockquote>
<p>or <em>&ldquo;How to POST a form in Swift using URLSession&rdquo;</em></p>
</blockquote>
<p>These examples will be using <a href="http://httpbin.org">httpbin.org</a> to test our request.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#66d9ef">let</span> url = URL(string: <span style="color:#e6db74">&#34;https://httpbin.org/post&#34;</span>)
</span></span></code></pre></div><h3 id="create-a-urlrequest-object-using-the-url">Create a <code>URLRequest</code> object using the URL</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#66d9ef">var</span> request = URLRequest(url: url!)
</span></span><span style="display:flex;"><span>request.httpMethod = <span style="color:#e6db74">&#34;POST&#34;</span>
</span></span><span style="display:flex;"><span>request.setValue(
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;application/x-www-form-urlencoded&#34;</span>,
</span></span><span style="display:flex;"><span>    forHTTPHeaderField: <span style="color:#e6db74">&#34;Content-Type&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><h3 id="create-a-list-of-key-values-to-be-sent">Create a list of key-values to be sent</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#66d9ef">let</span> queryItems = [
</span></span><span style="display:flex;"><span>            URLQueryItem(name: <span style="color:#e6db74">&#34;x&#34;</span>, value: <span style="color:#ae81ff">123</span>),
</span></span><span style="display:flex;"><span>            URLQueryItem(name: <span style="color:#e6db74">&#34;y&#34;</span>, value: <span style="color:#e6db74">&#34;hello&#34;</span>),
</span></span><span style="display:flex;"><span>            URLQueryItem(name: <span style="color:#e6db74">&#34;z&#34;</span>, value: <span style="color:#e6db74">&#34;world&#34;</span>),
</span></span><span style="display:flex;"><span>        ]
</span></span></code></pre></div><h3 id="construct-the-body-of-the-post-request">Construct the body of the POST request</h3>
<p>The <code>URLComponents</code> class can be used to construct the body of the <code>HTTP POST</code> request.
This is generally used to construct the query params for URLs for <code>HTTP GET</code> requests.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#75715e">// Create a URLComponents object using blank url</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> urlComponents = URLComponents(string: <span style="color:#e6db74">&#34;&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Assign the query items to the object</span>
</span></span><span style="display:flex;"><span>urlComponents?.queryItems = queryItems
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Fetch the formatted string from the URLComponents object</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> request_body = urlComponents!.query!
</span></span></code></pre></div><h3 id="set-the-body-of-the-request-variable">Set the body of the <code>request</code> variable</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span>request.httpBody = Data(request_body.utf8)
</span></span></code></pre></div><h3 id="make-the-request-using-urlsession">Make the request using <code>URLSession</code></h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span>URLSession.shared.dataTask(with: request) {(data, response, error) <span style="color:#66d9ef">in</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Decode response from the data variable using JSONDecoder</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div>]]></content:encoded></item><item><title>Encoding and Decoding JSON in Swift</title><link>https://www.define.run/posts/encoding-and-decoding-json-in-swift/</link><pubDate>Sun, 14 Jul 2019 00:00:00 +0000</pubDate><guid>https://www.define.run/posts/encoding-and-decoding-json-in-swift/</guid><description><![CDATA[ <blockquote>
<p>aka <em>&ldquo;How to decode response from a JSON API&rdquo;</em></p>
</blockquote>]]></description><content:encoded><![CDATA[ <blockquote>
<p>aka <em>&ldquo;How to decode response from a JSON API&rdquo;</em></p>
</blockquote>
<p>Import the <code>Foundation</code> library. This will also work if <code>SwiftUI</code> or <code>UIKit</code> or imported.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#a6e22e">Foundation</span>
</span></span></code></pre></div><h4 id="define-a-struct-to-represent-the-json-data">Define a struct to represent the JSON data</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#66d9ef">struct</span> <span style="color:#a6e22e">Dog</span>: Codable {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> name: String
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> age: Int
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The struct should use the <code>Codable</code> protocol. This is required both for encoding and decoding.</p>
<ul>
<li>We encode an object that respects the <code>Codable</code> protocol.</li>
<li><code>OR</code> we decode an object that respects the <code>Codable</code> protocol.</li>
</ul>
<h2 id="decoding-from-json">Decoding from JSON</h2>
<p>The following is the JSON used in the example.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#66d9ef">let</span> json = <span style="color:#e6db74">&#34;&#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">{&#34;</span>name<span style="color:#e6db74">&#34;: &#34;</span>Tommy<span style="color:#e6db74">&#34;, &#34;</span>age<span style="color:#e6db74">&#34;: 6}
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">&#34;&#34;&#34;</span>
</span></span></code></pre></div><p>Create a <code>Data</code> object to store the JSON data</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#66d9ef">let</span> data = Data(json.utf8)
</span></span></code></pre></div><p>The example uses JSON input from a variable. API responses will be <code>Data</code> objects, so they don&rsquo;t have to be converted into a <code>Data</code> object again.</p>
<h4 id="initialize-a-json-decoder">Initialize a JSON decoder</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#66d9ef">let</span> decoder = JSONDecoder()
</span></span></code></pre></div><h4 id="decode-the-json-into-an-object">Decode the JSON into an object</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#66d9ef">let</span> decoder = decoder.decode(Dog.<span style="color:#66d9ef">self</span>, from: data)
</span></span></code></pre></div><p>The Xcode will warn about this expression throwing. So we&rsquo;ll have to wrap it with a <code>try</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#66d9ef">let</span> decoder = <span style="color:#66d9ef">try</span>? decoder.decode(Dog.<span style="color:#66d9ef">self</span>, from: data)
</span></span></code></pre></div><p><code>decoder</code> will now be a <code>JsonResponse</code>  object and will contain the fields <code>x</code> and <code>y</code></p>
<h2 id="encoding-to-json">Encoding to JSON</h2>
<p>Represent a new dog.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#66d9ef">let</span> dog = Dog(name: <span style="color:#e6db74">&#34;Peggy&#34;</span>, age: <span style="color:#ae81ff">3</span>)
</span></span></code></pre></div><p>Define an encoder</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#66d9ef">let</span> encoder = JSONEncoder()
</span></span></code></pre></div><p>Encode the dog and ofcourse wrap it with a <code>try</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#66d9ef">let</span> json = <span style="color:#66d9ef">try</span> encoder.encode(dog)
</span></span></code></pre></div><p>The result will be a <code>Data</code> object. The response can be verified by converting it to String.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span>print(String(data: encoded_json, encoding: .utf8)<span style="color:#f92672">!</span>)
</span></span></code></pre></div><h2 id="stuff-to-know">Stuff to know</h2>
<p>In the <code>Dog</code> struct, both <code>name</code> and <code>age</code> are defined as mandatory properties. This means that the intializer for Dog will expect both the properties to be passed to the initializer when creating the object.</p>
<p>If we expect JSON responses from APIs to be containing optional fields, then it is better to make the properties optional too. Example below indicates that the <code>age</code> field is optional.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-swift" data-lang="swift"><span style="display:flex;"><span><span style="color:#66d9ef">struct</span> <span style="color:#a6e22e">Dog</span>: Codable {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> name: String
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> age: Int?
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div>]]></content:encoded></item></channel></rss>