<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Vedang.me, Only Posts</title>
  <link href="https://vedang.me/feeds/posts.xml" rel="self"/>
  <link href="https://vedang.me/"/>
  <updated>2026-08-31T02:14:13+00:00</updated>
  <id>https://vedang.me/</id>
  <author>
    <name>Vedang Manerikar</name>
  </author>
  <entry>
    <id>https://vedang.me/compiling-emacs-on-ubuntu/</id>
    <link href="https://vedang.me/compiling-emacs-on-ubuntu/"/>
    <title>Compiling Emacs Master Branch from source on Ubuntu</title>
    <updated>2025-01-01T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>Compiling Emacs from source is non-trivial. Here are the steps for the interested reader:</p><p><div class="orgtoc"></p><ul><li><a href='#orgbf0be2d'>Get the source-code</a></li><li><a href='#org39d8b36'>Make sure you have the all the dependencies installed</a></li><li><a href='#org68de58c'>Compile Emacs!</a></li><li><a href='#org023de49'>Misc notes</a></div></li></ul><p><a id="orgbf0be2d"></a></p><h1 id="get_the_source-code">Get the source-code</h1><pre><code class="language-fish">git clone git://git.sv.gnu.org/emacs
</code></pre><p>This step takes a long time, because the Emacs source repository is giant. But I recommend cloning the full repo:</p><ul><li>It's easy to stay updated with the latest changes with <code>git fetch</code> in the future.</li><li>Telling Emacs where the C source code is let's us jump all the way down into primitive functions when exploring elisp functions.<pre><code class="language-emacs-lisp">    &#40;setq source-directory &#40;expand-file-name &quot;/&lt;path&gt;/emacs/&quot;&#41;&#41;
    &#40;setq find-function-C-source-directory &#40;expand-file-name &quot;/&lt;path&gt;/emacs/src/&quot;&#41;&#41;
    </code></pre></li></ul><p><a id="org39d8b36"></a></p><h1 id="make_sure_you_have_the_all_the_dependencies_installed">Make sure you have the all the dependencies installed</h1><p>The easiest way to install almost all the dependencies is to run the following command:</p><pre><code class="language-fish">sudo apt build-dep emacs
</code></pre><p>This command uses the information in the Ubuntu package management system. In my experience today (<span class="timestamp-wrapper"><span class="timestamp">[2025-01-01 Wed]</span></span>), this command installed everything other than <code>libgccjit</code> and <code>libtree-sitter</code>. Let's install those now:</p><h2 id="installing_libgccjit">Installing libgccjit</h2><ol><li>Check your gcc version with <code>gcc --version</code>.</li><li>You need to install <code>libgccjit-&lt;x&gt;-dev</code> where <code>&lt;x&gt;</code> is your gcc version.<pre><code class="language-fish">sudo apt install libgccjit-11-dev
</code></pre></li></ol><h2 id="installing_libtree-sitter">Installing libtree-sitter</h2><pre><code class="language-fish">sudo apt install libtree-sitter-dev
</code></pre><p><a id="org68de58c"></a></p><h1 id="compile_emacs%21">Compile Emacs!</h1><pre><code class="language-fish"># Generate Configure and Make scripts:
./autogen.sh
# Run the Configure command:
./configure --without-toolkit-scroll-bars --with-x-toolkit=gtk3 --with-xpm=ifavailable --with-jpeg=ifavailable --with-gif=ifavailable --with-tiff=ifavailable --with-xml2 --with-rsvg --without-pop --with-png --with-mailutils --with-native-compilation --with-cairo --with-harfbuzz --with-tree-sitter --with-sqlite3
# If everything works:
make -j8 bootstrap
# If everything works:
make -j8
# Test the binary:
src/emacs -Q
# Install the binary:
sudo make install
</code></pre><p><a id="org023de49"></a></p><h1 id="misc_notes">Misc notes</h1><ul><li>Running <code>./configure</code> will output the default flags that Emacs plans to use.<ul><li>If you look at the last section of what <code>configure</code> prints, you can check if there are new options which are not covered by my configure command above.</li></ul></li><li>To restart compilation from an absolutely clean slate, run <code>make distclean</code></li><li>To know the max number of cores you can run parallel compilation on, run <code>nproc</code>. This is the max value for the <code>j</code> parameter in the <code>make</code> command.</li></ul>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/tips-for-better-presentations/</id>
    <link href="https://vedang.me/tips-for-better-presentations/"/>
    <title>Simple, but not easy, tips for Better Presentations</title>
    <updated>2024-10-15T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><em>I'm one of the editors of Rootconf 2024. This post comes from conversations in feedback sessions with potential speakers. If you are on the fence about submitting your systems engineering story, <a href='https://hasgeek.com/rootconf/2024/sub'>go ahead and submit</a>! We'll workshop the talk with you!</em></p><p><em>Cross-posted from <a href='https://open.substack.com/pub/unraveltech/p/simple-but-not-easy-tips-for-better?r=46gtts&utm_campaign=post&utm_medium=web&utm_source=vedang'>Unravel.tech's blog</a>, where I published this piece originally.</em></p><p>My first public presentation was a terrible disaster! Elasticsearch was new and I was one of two speakers at the first Elastic-organised meetup in Bangalore. I decided I needed to impress folks with <em>how much</em> I knew. Everyone went to sleep by minute 5.</p><p>If you've submitted a talk or been invited to speak &#x2013; Congratulations!</p><p>Congratulations on getting over the fear of public speaking! You've submitted a talk proposal. You've already won half the battle. In this post, I want to help you with the other half.</p><p><a id="h:73A8E4C6-EEA7-4AA1-974F-42271462CB39"></a><h1 id="the_summary">The Summary</h1></p><p>My treacherous brain firmly believes that the best time to create slides is last-minute. <strong>It is not</strong>. If your brain is like mine and you are panicking right now, here's the gist, in order of priority:</p><ol><li>What's the story? Channel that one friend of yours who tells great stories!</li><li>Start the story when the bear is just about to eat you! People + problems = Pavlovian response.</li><li>What is your audience's need? When <em>they</em> retell the story, what will they gush about?</li><li>Less is more, bigger is better. Double the font size! Avoid information dumping.</li><li>Don't just read out what the slide says. We can read faster than you can speak.</li><li>Use a timeline as a forcing function.</li><li>Repeating yourself is important to drive the point home. To drive the point home, Repetition is key!</li></ol><p>Okay. Go get those slides done!</p><p>Or dive into the details. If time permits!</p><p><div class="orgtoc"></p><ul><li><a href='#h:73A8E4C6-EEA7-4AA1-974F-42271462CB39'>The Summary</a></li><li><a href='#h:B1D4DBBF-39FB-4490-8CD9-9A67ED192B3D'>What's the story?</a></li><li><a href='#h:AFB7D8BF-0199-4E65-98DB-9BF8B409619D'>Start the story when the bear is just about to eat you!</a></li><li><a href='#h:18FD9AE9-7B4B-4827-8FBC-BA2F89BDEF63'>What is your audience's need?</a></li><li><a href='#h:A361F9F3-64DB-4BE8-931E-E3C9D6ACF5DE'>Less is more</a></li><li><a href='#h:4FD01D28-9BF0-45DD-8016-F972EE995534'>Don't just read out what the slide says.</a></li><li><a href='#h:E625DB22-96A8-47B0-9811-F1FCA9DD3961'>Use a timeline</a></li><li><a href='#h:0B380643-68D6-4657-BC75-873014053059'>Repeating yourself is important to drive the point home.</a></li><li><a href='#h:EBD05DC8-6DF1-42FE-B8B9-3B908843F759'>Pro-tip: Artificial constraints make you better</a></li><li><a href='#h:A52086BA-3239-436A-940D-CD801D34EF94'>My own improvement path: Use your body, look at everyone.</a></li><li><a href='#h:B265B0A3-C45F-43E6-9DEC-E3EC9AF81B4D'>Closing note: Practice, practice, practice</a></div></li></ul><p><a id="h:B1D4DBBF-39FB-4490-8CD9-9A67ED192B3D"></a><h1 id="what%27s_the_story%3F">What's the story?</h1></p><p>As engineers, our instinct is to define everything and share all the information in our head. But <strong>dumping information is completely unhelpful</strong>. Think back to the last talk you loved. Why did you remember it? I bet it connected with you!</p><p>Telling a story helps people connect with you and remember your insights. Say outrageous things! Use interesting visuals! Let your story take surprising and unexpected turns!</p><blockquote><p> AT has the ability to hook the company on slide 2 and keep them hooked for however long he wants. Not a single dull All-Hands. </p><p> KK plans his presentations meticulously, down to the last detail he wants to hammer home. He'll rework and pare down those slides until only the essential remains. </p><p> What they both have in common is that the story is always so interesting! </p><p> ~ a note from a journal entry I wrote long ago. </p></blockquote><p>Audiences want to be entertained and intrigued. They want to feel like they are with you on every step of a hero's journey. What are you doing to make your presentation a movie-like experience?</p><p><a id="h:AFB7D8BF-0199-4E65-98DB-9BF8B409619D"></a><h1 id="start_the_story_when_the_bear_is_just_about_to_eat_you%21">Start the story when the bear is just about to eat you!</h1></p><p>Don't bother with introductions, definitions, background. These can come later in the presentation. <em>Start the story when the bear is just about to eat you!</em> <strong>Introduce the problem, the mystery first</strong>! Make it a question that your audience is trying to solve ahead of you.</p><p>From Kent Beck<sup><a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink">1</a></sup></p><blockquote><p> Programmers have a Pavlovian engineering response. </p><p> Pose them a problem and they'll start trying to solve it. </p><p> Give them a chance to co-engineer along with your presentation by making sure the first bite gets their saliva flowing. Then you can explain the rest of the problem and your brilliant solution knowing that they are there along with you. </p><p> ~ Kent Beck </p></blockquote><p><a id="h:18FD9AE9-7B4B-4827-8FBC-BA2F89BDEF63"></a><h1 id="what_is_your_audience%27s_need%3F">What is your audience's need?</h1></p><p>What people <strong>really want</strong> when they attend a conference is <strong>to be able to look cool later</strong>. They want to share the cool things they saw and learnt. A great presentation is something they can gush about to all those who missed the conference.</p><p>Put yourself in the shoes of your audience. Ask:</p><ul><li>Am I speaking at a complexity level that works for 80% of the audience?</li><li>What are the takeaways from my talk? Can I say them in a clever and surprising way?</li><li>What is a non-obvious trade-off my solution makes?</li></ul><p>Try and answer each and every one of these.</p><p><a id="h:A361F9F3-64DB-4BE8-931E-E3C9D6ACF5DE"></a><h1 id="less_is_more">Less is more</h1></p><p>You need to go slower than you think, because fast and good is really tough to get right.</p><p>"Go Slower" should also be interpreted as "Cover less ground". Yes, you know many wonderful things about this topic. But you'll have chances to speak on the topic again<sup><a id="fnr.2" class="footref" href="#fn.2" role="doc-backlink">2</a></sup>. Don't cram everything you know into your allotted time slot. Zoom into one particular thing and weave a story around it.</p><p>The less the content on your slide, the better. Put only the most important nugget on the slide.</p><p><a id="h:4FD01D28-9BF0-45DD-8016-F972EE995534"></a><h1 id="don%27t_just_read_out_what_the_slide_says.">Don't just read out what the slide says.</h1></p><p>This is the most common presentation advice, and yet it's amazing how often it's completely ignored.</p><p>YOUR.SLIDES.ARE.NOT.YOUR.TELEPROMPTER</p><p>From Wikipedia, with minor edits and emphasis<sup><a id="fnr.3" class="footref" href="#fn.3" role="doc-backlink">3</a></sup>:</p><blockquote><p> The term "PowerPoint karaoke" is&#x2026; derisively used to refer to <strong>presenters who face the screen</strong> where their PowerPoint slides are being projected and proceed to read them, <strong>boring and effectively ignoring their audience</strong>. </p></blockquote><p>If you put everything on the slide, your audience will just read the content and stop listening to you altogether. And since humans can read much faster than we can speak, your audience will be zoned out and bored.</p><p>Instead, your slide should have something interesting on it, which helps the audience pay closer attention to <span class="underline">what you are saying</span>.</p><p><a id="h:E625DB22-96A8-47B0-9811-F1FCA9DD3961"></a><h1 id="use_a_timeline">Use a timeline</h1></p><p>A timeline is an incredibly powerful device to help you plan your presentation. Writing a timeline automatically shows you which parts are tight and which are weak. It also gives you a framework to practice in.</p><p>Geoffrey was prepping for a 10-minute talk, and look at how he has planned those 10 minutes<sup><a id="fnr.4" class="footref" href="#fn.4" role="doc-backlink">4</a></sup>:</p><blockquote><p> Here’s another good timeline, submitted by Geoffrey Litt as part of his proposal for <a href='https://www.youtube.com/watch?v=RhUmSeko1ZE'>"ENHANCE! Upscaling Images with Neural Networks"</a>: </p><p> – (1min) Introduction </p><p><ul><li>We can now “enhance” photos!!</li><li>Show examples from CSI and from a 2016 research paper</p><p> – (2min) A very brief neural networks crash course </p><p></li><li>A taxonomy of neural networks: Treating a single neural network as a black box, what are the different types of inputs and outputs that are possible?</li><li>Training a neural network: How does a neural network learn?</p><p> – (2min) GANs: inspired by art forgery </p><p></li><li>Human art forgers are in a constant battle with the police. As the detection techniques get better, so do the forgers.</li><li>By creating multiple neural networks (a “forger” and a “detector”) and pitting them against each other, we end up with a really good forger!</p><p> – (2min) Training a GAN </p><p></li><li>Explain how the GAN training process works using visuals</li><li>Show examples of the output getting better as the network trains</p><p> – (3min) Cool applications of GANs </p><p></li><li>Converting text to images!</li><li>Converting line drawings to photographs!</li><li>Converting images to 3D models!</p><p> Like Tara’s timeline, Geoffrey’s is detailed enough to make it clear that he’s comfortable with his topic of choice and has thought about how to use the time effectively, and it sticks to the facts (while still conveying his excitement about the topic). </li></ul></p></blockquote><p><a id="h:0B380643-68D6-4657-BC75-873014053059"></a><h1 id="repeating_yourself_is_important_to_drive_the_point_home.">Repeating yourself is important to drive the point home.</h1></p><p>Repeat yourself. Repetition is key!</p><p>From Zach Holman<sup><a id="fnr.5" class="footref" href="#fn.5" role="doc-backlink">5</a></sup>:</p><blockquote><p> Humans love repetition. It’s one of the tricks used in lots of disciplines. </p><p> The best jazz soloists are capable of starting a melody, repeating it, and then playing with it after the audience has identified with the repetition. </p><p> The best storytellers will repeat the same line throughout a story to build a sense of familiarity, of excitement. </p><p> Slides are no different. </p></blockquote><p>Repeat yourself. Repetition is key!</p><p>Repetition is the shizz. Those important bits? Say them again and again.</p><p><a id="h:EBD05DC8-6DF1-42FE-B8B9-3B908843F759"></a><h1 id="pro-tip%3A_artificial_constraints_make_you_better">Pro-tip: Artificial constraints make you better</h1></p><p>Identify the constraint that really tickles your mind!</p><p>One of my favourite constraints is &#x2013; "Spend only 20 seconds per slide". It comes from the super fun PechaKucha style of presentations<sup><a id="fnr.6" class="footref" href="#fn.6" role="doc-backlink">6</a></sup>, where speakers have to make only 20 slides, and spend only 20 seconds on each slide (slides are auto-forwarded!)</p><p>I am a big fan of constraints. Think how Twitter's 140 char constraint helped you create superb content.</p><p>Remember that this is a pro-tip. Use it once you've worked through everything else in this post.</p><p><a id="h:A52086BA-3239-436A-940D-CD801D34EF94"></a><h1 id="my_own_improvement_path%3A_use_your_body%2C_look_at_everyone.">My own improvement path: Use your body, look at everyone.</h1></p><p>When I look at great speakers, here is what they do consistently: "be loose" on stage.</p><p>Being able to ad-lib on stage and be present for everyone, without losing the rhythm of the talk or forgetting where you are requires a tremendous amount of practice. I'm not there yet, but no reason why you can't crack it you smart cookie!</p><p><a id="h:B265B0A3-C45F-43E6-9DEC-E3EC9AF81B4D"></a><h1 id="closing_note%3A_practice%2C_practice%2C_practice">Closing note: Practice, practice, practice</h1></p><p>Here is my practice schedule right now:</p><ol><li>Get the slides to a point where they are reasonably complete. Follow the tips I've written above.</li><li>Say them out loud. Change them because they are so bad.</li><li>Do this 2-3 times.</li><li>Present them to some friends. Change them because friends point out how bad they are.</li><li>Repeat step 4 at least one more time!</li></ol><p>Extra steps: I practice by recording myself speaking, but the next time I am going to practice by recording myself moving. I fidget on stage, and I need to fix it. I wish I'd recorded myself moving earlier!</p><p>Best of luck! I know you are going to crush it!</p><p><hr> <em>This post is the result of a series of conversations with my fellow editors: <a href='https://x.com/shraddhaag'>Shraddha Agrawal</a> and <a href='https://x.com/sphirani'>Saurabh Hirani</a>. Many thanks to them for crystallizing these ideas.</em></p><p><em>As always, my friends <a href='https://x.com/prajwalit'>Prajwalit</a>, <a href='https://x.com/kiran_kulkarni/'>Kiran</a> and <a href='https://x.com/KapilReddy'>Kapil</a> gave me feedback on initial drafts of the post. Thank you!</em> <hr></p><h2 id="footnotes">Footnotes</h2><p><sup><a id="fn.1" class="footnum" href="#fnr.1">1</a></sup> Kent Beck's full post is totally worth reading: <a href='https://tidyfirst.substack.com/p/start-presentations-on-the-second'>Start presentations on the second slide</a></p><p><sup><a id="fn.2" class="footnum" href="#fnr.2">2</a></sup> The irony of saying this as part of a 2000 word essay is not lost on me. Do you want help or not?</p><p><sup><a id="fn.3" class="footnum" href="#fnr.3">3</a></sup> This quote comes from <a href='https://en.wikipedia.org/wiki/PowerPoint_karaoke#Other_uses'>the Wikipedia article on Powerpoint Karaoke</a>, which is actually a fun game where presenters present slides they have never seen before.</p><p><sup><a id="fn.4" class="footnum" href="#fnr.4">4</a></sup> For more examples and a detailed explanation, read Lindsey Kuper's blogpost: <a href='https://decomposition.al/blog/2017/06/30/how-to-write-a-timeline-for-a-bangbangcon-talk-proposal/'>How to write a timeline for a BangBangCon talk proposal</a></p><p><sup><a id="fn.5" class="footnum" href="#fnr.5">5</a></sup> Zach also covers useful points on Slide design his post: <a href='https://zachholman.com/posts/slide-design-for-developers/'>Slide Design for Developers</a></p><p><sup><a id="fn.6" class="footnum" href="#fnr.6">6</a></sup> Read more about PechaKucha <a href='https://en.wikipedia.org/wiki/PechaKucha'>on Wikipedia</a>.</p>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/performance-is-orthogonal-to-typing/</id>
    <link href="https://vedang.me/performance-is-orthogonal-to-typing/"/>
    <title>Performance is orthogonal to Typing</title>
    <updated>2024-08-19T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><em>I wrote this essay as part of a discussion on the Engineering Org discord channel. Posting it here with light editing and some context.</em></p><h1 id="the_context_for_the_conversation">The context for the conversation</h1><p>The conversation started with @whiterose sharing <a href='https://x.com/nathanmarz/status/1823446898122092720'>this tweet from Nathan Marz</a>:</p><p><tweet url="https://x.com/nathanmarz/status/1823446898122092720"></p><blockquote><p> Static typing is better for large codebases if your development team is mediocre or weak. Dynamic typing is far superior for a strong team. If you think static typing is better, then there are huge improvements you can make in how you structure your code and how you test. </p></blockquote><p></tweet></p><p>This tweet blew up on the internet, and Nathan Marz later posted a tweet that was <a href='https://x.com/nathanmarz/status/1824490570829664760'>much more nuanced and less controversial</a>:</p><p><tweet url="https://x.com/nathanmarz/status/1824490570829664760"></p><blockquote><p> Some general responses to the comments on my last tweet, which blew up way more than I expected: </p><p><ul><li>Another way to phrase what I said: all else being equal, dynamically typed languages have a higher ceiling and lower floor than statically typed languages. Most of the negative responses were about that lower floor, which I agree with. This is why dynamically typed languages require more skill.</li><li>Many strong teams use statically typed languages, and many weak teams use dynamically typed ones. I didn't say otherwise.</li><li>Not all dynamically typed languages are equal. Clojure/ClojureScript are much better languages than Javascript for reasons that have nothing to do with typing, and a strong team will tend to choose a better language.</li><li>Static types do not reduce the number of tests that are needed in any meaningful way, as they do nothing to help sample the input space of a function/component/system and assert on its effects. At best they eliminate the occasional assertion on an output type, but that's needed a lot more rarely than you think.</li><li>Static typing does not give a performance advantage. If there's a performance difference between languages, it's not because of static typing.</li></ul></p></blockquote><p></tweet></p><p>I responded to @whiterose that he should read this nuanced tweet instead, and he had a question: "Does the last point hold true? I remember you mentioning not to use Clojure where performance is needed in IN/Clojure". What follows is my answer to his question.</p><h1 id="dynamic_typing_languages_are_also_%22typed%22">Dynamic typing languages are also "typed"</h1><p>Okay, let me try to expand on this.</p><p>Firstly, dynamic typed languages are also "typed". Some are strongly typed, meaning you can define the exact type that you want something to be, and the runtime will enforce a check and throw an error if types do not match. Type conversion is well-defined. For example, you cannot do <code>1 + &quot;hello&quot;</code> in Clojure (strong typing), but you can in Javascript (weak typing).</p><p>Static typing by definition is strongly typed.</p><h1 id="performance_benefits_come_from_multiple_places">Performance benefits come from multiple places</h1><p>Now let's talk about Performance Optimisation. There are many places where we can get performance benefits from, but here are the top 3 that come to my mind:</p><h2 id="the_design_of_the_language_itself.">The design of the language itself.</h2><p>This is the data-structure and algorithm implementations for things in the standard library &#x2013; stuff we take for granted when using the language.</p><p>For example: Clojure has incredibly well-written immutable data-structures. This means you could run it in crazy multi-threaded cases but not have problems of data corruption. To compare, Scala had bugs in the standard library Map data-structure which would show up under high concurrency, leading to the wrong data existing in your hashmaps! Imagine what the reaction of the person debugging the issue must have been.</p><p>The trade-off here is performance. Clojure's data-structures are best-in-class implementations of immutable data-structures. But mutability gives huge performance gains. So if you are working on use-cases where performance is the real bottleneck, then you would need to write your own implementations of the standard data-structures for that gain. This is technically possible, but not practical.</p><h2 id="compiler_optimisations.">Compiler optimisations.</h2><p>This is where the compiler analyses the code you have written, and rewrites it for performance. There are a large number of tricks here, only some of which are related to typing. For example, you may have heard of loop unrolling, function inlining. These are not related to typing. But optimising variable assignment to registers, some forms of dead-code elimination, method dispatch are related to typing.</p><p>Clojure, Scala and Java are all compiled to bytecode, which runs on the JVM. But the optimisations possible are different, based on the explicit type information that is available and the implicit type information that the compiler derives. You could, if you want, give type hints to every part of the Clojure code you write. But this would be extra work, and not part of the job.</p><h2 id="runtime_optimisations">Runtime optimisations</h2><p>This is what the "Runtime" does for you. For C, Rust, Zig programs, the runtime is the Operating System itself. For Clojure, Java, Scala programs, the runtime is the JVM, which is running on top of the Operating System. For Javascript, the runtime is the Browser, which is running on top of the Operating System. You see where this is going.</p><p>The JVM and V8 engines have done some incredible work like JIT, Garbage collection tuning, which makes the runtime really fast, as compared to say Python.</p><p>The runtime gives you a lot of performance. This is why the new programming language Bend is so exciting, because it's a high-level language that runs directly on the GPU, which is whole new performance unlock.</p><h1 id="final_thoughts">Final thoughts</h1><p>Re: My offhand comment "not to use Clojure if you want performance", I hope I have explained a bit better above. Clojure can be every bit as performant as anything else that runs on the JVM. But you need to actually know what you are doing to achieve that.</p><p>That's also one of the benefits of using Rust or Zig over C. If you know what you are doing, you will be able to write the fastest stuff in C. But if two programmers both don't know how to program for performance, and one uses C and the other uses Rust/Zig, there is a better chance that the Rust/Zig guy will write the more performant code.</p><p>Caveat: I am <strong>not</strong> someone who can program high performance applications. I have never worked in a financial trading firm or a game development shop or anything like that. My knowledge is just cobbled together from my experiences as a web developer and noob systems developer. But I do think that it's good to keep performance in mind when designing applications. I want the thing I write to be fast.</p><p>Happy to learn from everyone here!</p>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/create-a-list-of-questions-before-you-open-the-book/</id>
    <link href="https://vedang.me/create-a-list-of-questions-before-you-open-the-book/"/>
    <title>Create a list of questions before you open the book</title>
    <updated>2024-05-14T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>Come up with atleast 10 questions of your own before starting to read. This will:</p><ul><li>Anchor you to the reading and allow the knowledge to sink in. (known as <a href='https://en.wikipedia.org/wiki/Priming_(psychology'>"The Priming Effect"</a>))</li><li>Keep you hooked to the book!</li></ul><p>Example questions you can ask (generic):</p><ul><li>What is the book really about?</li><li>What is being said in detail and how?</li><li>Is this book true in whole or part?</li><li>What do I make of the book?</li></ul><p>Come up with your own specific questions before diving into the book as well!<sup><a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink">1</a></sup></p><h2 id="footnotes">Footnotes</h2><p><sup><a id="fn.1" class="footnum" href="#fnr.1">1</a></sup> For a general introduction to speed-reading, see: <a href="denote:20230729T114325.html" class="internal-link">Learn Speed Reading to accelerate your learning</a></p>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/tsp-find-all-elisp-files-not-containing-lexical-binding-directive/</id>
    <link href="https://vedang.me/tsp-find-all-elisp-files-not-containing-lexical-binding-directive/"/>
    <title>Tiny Shell Puzzle: Find all elisp files in my config which don't have a lexical-binding directive</title>
    <updated>2024-04-17T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><div class="orgtoc"></p><ul><li><a href='#org15d0456'>Find all files which <span class="underline">do not</span> contain the phrase</a></li><li><a href='#org0c4e37a'>Add the directive to the first line of each file</a></li><li><a href='#h:57B58CC7-105F-4ADF-B13F-1F95AE2930F1'>The final answer, with an explanation of what each line does</a></li><li><a href='#org808f55e'>Searching only in the first line of the file</a></div></li></ul><p>For reasons unrelated to this post, the phrase <code>-&#42;- lexical-binding: t -&#42;-</code> needs to exist as a comment in the first line of all my Emacs Lisp files.</p><p>This is what the first line of most of my elisp files looks like:</p><p><code>;;; init-isearch.el --- Configuring built-in isearch the way I like it</code></p><p>The line starts with <code>;;;</code> (the comment syntax in Elisp), followed by the name of the file, and then a description of what the file contains.</p><p>This line needs to change to the following:</p><p><code>;;; init-isearch.el --- Configuring built-in isearch the way I like it -&#42;- lexical-binding: t -&#42;-</code></p><p>The caveats are as follows:</p><ol><li>Some files already contain this directive, so they shouldn't be modified</li><li>Some files do not start with <code>;;;</code>, these should be ignored.</li></ol><p>With those caveats in place, here is the tiny shell puzzle:</p><ol><li>Find all the files which need to be modified.</li><li>Modify all the files, writing the change back to disk</li></ol><p>This is a great example of the kind of one-off tasks that shell scripting is great for!</p><p>Once you've given it a try, scroll down to see my answer.</p><p><a id="org15d0456"></a></p><h1 id="find_all_files_which_%3Cspan_class%3D%22underline%22%3Edo_not%3C%2Fspan%3E_contain_the_phrase">Find all files which <span class="underline">do not</span> contain the phrase</h1><p>I use <code>ripgrep</code> for all my searching, and it gives me a handy way of solving this part of the problem:</p><pre><code class="language-fish">rg -t elisp --files-without-match &quot;lexical-binding&quot;
</code></pre><p><a id="org0c4e37a"></a></p><h1 id="add_the_directive_to_the_first_line_of_each_file">Add the directive to the first line of each file</h1><p>My mind immediately jumped to using <code>awk</code>, which is my goto power tool for modifying file content. This problem is actually easier to solve with <code>sed</code>, but I've dropped <code>sed</code> entirely from my toolkit (in favour of <code>awk</code>).</p><pre><code class="language-fish">gawk '
&#40;NR==1 &amp;&amp; /&#94;;;;/&#41;{ print $0 &quot; -&#42;- lexical-binding: t -&#42;-&quot; };
&#40;NR==1 &amp;&amp; !/&#94;;;;/&#41;{ print $0 };
&#40;NR&gt;1&#41;{ print $0 };
'
</code></pre><p><a id="h:57B58CC7-105F-4ADF-B13F-1F95AE2930F1"></a></p><h1 id="the_final_answer%2C_with_an_explanation_of_what_each_line_does">The final answer, with an explanation of what each line does</h1><pre><code class="language-fish">1  rg -t elisp --files-without-match &quot;lexical-binding&quot; | \
2  xargs -I {} \
3  gawk -i inplace '
4  &#40;NR==1 &amp;&amp; /&#94;;;;/&#41;{ print $0 &quot; -&#42;- lexical-binding: t -&#42;-&quot; };
5  &#40;NR==1 &amp;&amp; !/&#94;;;;/&#41;{ print $0 };
6  &#40;NR&gt;1&#41;{ print $0 };
7  ' {}
</code></pre><p>The meaning of each line is:</p><ul><li>Line 1: Use <code>rg</code> to find all fines which do not contain <code>lexical-binding</code>, and print only filenames.</li><li>Line 2: Pipe the filenames to <code>xargs</code> for pushing one-by-one to <code>awk</code>.</li><li>Line 3: Use the <code>-i inplace</code> switch to edit the file instead of printing to stdout.</li><li>Line 4: If line number is 1 and the line starts with <code>;;;</code>, add <code>-&#42;- lexical-binding: t -&#42;-</code> to the end of the line.</li><li>Line 5: If line number is 1 but the line <span class="underline">does not</span> start with <code>;;;</code>, just print the line as-is.</li><li>Line 6: For all other lines, just print the line as-is.</li><li>Line 7: This is where <code>xargs</code> will punch in the filename from our search (replacing <code>{}</code> with the filename)</li></ul><p>There is a bug in the first line &#x2013; our search query. Can you spot it? See if you can, and then scroll on to see the answer</p><p><a id="org808f55e"></a></p><h1 id="searching_only_in_the_first_line_of_the_file">Searching only in the first line of the file</h1><p>The bug is that we are looking for the phrase <code>lexical-binding</code> anywhere in the file. What we want is for it to exist in the first line of the file, and if it does not exist, we want to add it.</p><p>This bug meant I missed updating 3 files in my initial run.</p><p>I couldn't fix it using just <code>ripgrep</code>, so this was what I replaced the first line with. If you know a better solution, please get in touch!</p><pre><code class="language-fish">fd -e .el --exec sh -c 'head -n 1 {} | rg -qv lexical-binding &amp;&amp; echo {}'
</code></pre><p>Here, I use <code>fd</code> to find all emacs-lisp files, and then execute a small script on them. This script checks the first lines, and if the first line does not contain <code>lexical-binding</code>, it outputs the filename!</p>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/clojure-developer-tooling-for-speed-and-productivity/</id>
    <link href="https://vedang.me/clojure-developer-tooling-for-speed-and-productivity/"/>
    <title>In/Clojure 2024: Developer Tooling For Speed And Productivity In 2024!</title>
    <updated>2024-03-27T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><em>These are slides from <a href='https://www.youtube.com/watch?v=pVvuyaRDA58'>my talk at In/Clojure 2024</a>. These slides may not have all the context, they'll make better sense after you watch the talk.</em></p><p><em>If you have any problems using the tools I've mentioned here, or if you want help improving your Clojure programming workflow, drop me an email! (Or better still, <a href="denote:20240515T231227.html#h:99FDB94B-3889-41C8-B33C-101F206B6F13" class="internal-link">book a session with me</a>). I love talking to other Clojure programmers, exchanging tips and tricks, and learning from them.</em></p><p><div class="video-container"> <iframe src="https://www.youtube.com/embed/pVvuyaRDA58" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen> </iframe> </div></p><p><div class="orgtoc"></p><ul><li><a href='#h:FBD17B1F-CF68-4A5F-9631-D94DC9E20102'>Why this talk?</a></li><li><a href='#h:17658B7C-2A51-4854-A903-97D296112B57'>I want to write a new library or application!</a></li><li><a href='#h:7F6BCD43-5990-4AB4-AD76-6F46F2619680'>I use deps-new!</a></li><li><a href='#h:E1CA6BAD-7F09-4BAB-9161-4D4C5F56FEF0'>What deps-new gives me</a></li><li><a href='#h:FA35323A-C522-4CDB-9313-AA0619613264'>You can even write your own deps-new templates!</a></li><li><a href='#h:5FB73441-42F4-437E-AB47-D46755562B82'>What can we as clojure devs do?</a></li><li><a href='#h:E21AA902-7FB4-47C7-BF16-E755FBF2E4B8'>References to learn deps-new</a></li><li><a href='#h:28A24030-01E1-44EC-B1CA-5737CBF261B8'>I want to add more aliases to my repository!</a></li><li><a href='#h:42B7608D-EE98-478B-88B9-A368F661775E'>I use neil!</a></li><li><a href='#h:BCDC7604-ECD0-47E9-A309-411E52C049E8'>Examples of using neil!</a></li><li><a href='#h:7B40948C-CF1D-4970-9FED-F7F1BBF873F8'>Neil can do more than just add aliases!</a></li><li><a href='#h:7254A3DD-3BB1-404B-9466-1230861DB4B2'>Neil can do more than just add aliases!</a></li><li><a href='#h:7E82661A-CA5D-4B9B-8901-66CD54731B6D'>What can we as clojure devs do?</a></li><li><a href='#h:48270D61-BBD2-4323-8ADE-63AAA3438069'>References for learning more about neil</a></li><li><a href='#h:EB7B3E7E-5DAE-4A7A-98FB-C0395C659503'>Okay, I am now ready to spin up a REPL and connect to it!</a></li><li><a href='#h:FE043DED-0F32-4B50-9955-0D8087A7BD7F'>I'm happy I never have to figure this out again!</a></li><li><a href='#h:A0A4E361-EDE2-4021-8A11-E83757D83F27'>Templates for the win</a></li><li><a href='#h:4939A4F2-597E-4349-B21A-3CB8F18902D4'>I use structured logging everywhere!</a></li><li><a href='#h:9E2199EF-9BD7-437B-B0F7-B1380A269827'>I want beautiful Documentation</a></li><li><a href='#h:4D700447-D8AA-4A4E-A24F-3CEC66204E52'>I use Clerk: write beautiful notebooks <span class="underline">and</span> beautiful code-bases!</a></li><li><a href='#h:40958F52-414B-43E1-A87D-A21F8563EB2B'>Clerk: write beautiful notebooks <span class="underline">and</span> beautiful code-bases!</a></li><li><a href='#h:CA787ACA-39D7-485C-B3FC-C3087B32F2F1'>I use tagref: clear separation of code and documentation</a></li><li><a href='#h:E432FDCF-210A-48E2-8946-882477385755'>Documentation for the team: tagref</a></li><li><a href='#h:29C1F422-B93E-4347-ABEE-980B0D805130'>References for improving documentation</a></li><li><a href='#h:52C3A859-91F2-4E59-ADE9-E2B648BF1C0C'>I want to write clean, maintainable code!</a></li><li><a href='#h:5ED92E91-266F-47C4-B6B6-6F2EFBD0FB92'>clj-kondo: My recommendation</a></li><li><a href='#h:455C7D9C-E790-40B7-8D12-9D4A880DA807'>clj-kondo: My recommendation</a></li><li><a href='#h:C2FB456F-F43E-4D2D-8FDE-01D1254039F0'>cljfmt: My recommendation</a></li><li><a href='#h:ABB58979-B294-4E02-AFC4-37DA90646DB4'>What can we as clojure devs do?</a></li><li><a href='#h:091458FA-32EC-40E6-AC1E-A30687EC5C8F'>References for clj-kondo, cljfmt and zprint</a></li><li><a href='#h:238F58ED-D45D-4468-A6B6-01A4BD38268B'>Can we improve the REPL experience?</a></li><li><a href='#h:B8F12EBF-1B4B-422E-A07E-FA87F9D9B805'>Debugging like I've never experienced before: Flowstorm!</a></li><li><a href='#h:CAFE6CF8-C675-4D48-9B0A-C123AF6D4DB5'>Debugging with Flowstorm</a></li><li><a href='#h:6EF62F47-7E2E-410C-8EDF-A64A51D843E9'>References for learning more about Flowstorm</a></li><li><a href='#h:86DBFE60-028E-44D0-A2C8-8B7C18101E89'>Why Micro-services?</a></li><li><a href='#h:43403155-E5C8-4D73-8B82-8D68294CD27A'>Why Micro-services?</a></li><li><a href='#h:3FAC1EA3-ADE4-4912-855B-8EDE6FF65EF7'>Why Monorepos?</a></li><li><a href='#h:739109BD-0AC6-4343-A103-AA9021FF99EA'>So what goes wrong with Monorepos?</a></li><li><a href='#h:332F0BC1-BA71-4076-B69D-CD2C9BA95906'>I use Polylith from day one</a></li><li><a href='#h:7072163A-F7AB-41BC-9DC7-E831081E3500'>Let's create our Polylith monorepo!</a></li><li><a href='#h:ABCDF299-13C8-4091-AB6B-CBFC852B3497'>The terminology of Polylith: project</a></li><li><a href='#h:56735D24-1D67-4B39-952C-22BF2B6E2229'>The terminology of Polylith: base</a></li><li><a href='#h:EC537EEA-4435-4FB2-B6F4-9D9D883AF645'>The terminology of Polylith: component</a></li><li><a href='#h:4BE83BAC-62C4-4B1E-A4E8-F58F786BD0E7'>How does all this tie together?</a></li><li><a href='#h:464073B8-D6A3-427D-BED4-18A95B5872A1'>How Polylith promotes Modular architecture</a></li><li><a href='#h:E41E8FC5-2F54-4166-97B2-C8A019A7B320'>Polylith: getting the best of micro-services and mono-repos</a></li><li><a href='#h:7BA51976-ED7F-40E3-87F3-015E96B77351'>What does the poly tool give me?</a></li><li><a href='#h:D4FED61E-AAEA-411B-A7EA-2D9D186C7BC0'>References for learning more about Polylith</a></li><li><a href='#h:61FC33B2-E5AA-4DCC-A8A3-6C227E0FBA4B'>There is so much more &#x2026;</a></li><li><a href='#h:1838A668-A2BF-4B46-BA27-6A4CC1C1ECAA'>Come say hi!</a></div></li></ul><p><a id="h:FBD17B1F-CF68-4A5F-9631-D94DC9E20102"></a></p><h1 id="why_this_talk%3F">Why this talk?</h1><p>So you'll show me <strong>your cool workflows</strong>! (help me get better!) <hr></p><p><a id="h:17658B7C-2A51-4854-A903-97D296112B57"></a></p><h1 id="i_want_to_write_a_new_library_or_application%21">I want to write a new library or application!</h1><p>I need to figure out &#x2026;</p><ul><li>How to run my application</li><li>How to run all the tests in my project</li><li>How to build the artifacts of my app/lib</li><li>How to deploy these artifacts so others can use them</li></ul><h2 id="speaker_notes">Speaker Notes</h2><p>The Javascript ecosystem, for all the abuse we hurl at them, has done an incredible job. The templating that people have access to out of the box is something we should all aspire to! <hr></p><p><a id="h:7F6BCD43-5990-4AB4-AD76-6F46F2619680"></a></p><h1 id="i_use_deps-new%21">I use deps-new!</h1><p><a href="https://github.com/seancorfield/deps-new">https://github.com/seancorfield/deps-new</a></p><pre><code class="language-fish">clojure -Tnew lib :name me.vedang/depsnew&#95;lib
</code></pre><pre><code class="language-fish">tree depsnew&#95;lib
</code></pre><p>RESULTS:</p><pre><code>depsnew&#95;lib
├── CHANGELOG.md
├── LICENSE
├── README.md
├── build.clj
├── deps.edn
├── doc
│   └── intro.md
├── src
│   └── me
│       └── vedang
│           ├── depsnew&#95;lib.clj
└── test
    └── me
        └── vedang
            └── depsnew&#95;lib&#95;test.clj
</code></pre><p><hr></p><p><a id="h:E1CA6BAD-7F09-4BAB-9161-4D4C5F56FEF0"></a></p><h1 id="what_deps-new_gives_me">What deps-new gives me</h1><ul><li>All the boilerplate!</li><li>Clear Instructions in the README</li><li>Aliases:<ul><li><code>clojure -X:run-x</code>, <code>clojure -X:run-m</code> Run your app</li><li><code>clojure -T:build test</code> Run tests</li><li><code>clojure -T:build ci</code> Build artifacts</li><li><code>clojure -T:build deploy</code> Deploy artifacts</li></ul></li></ul><p><hr></p><p><a id="h:FA35323A-C522-4CDB-9313-AA0619613264"></a></p><h1 id="you_can_even_write_your_own_deps-new_templates%21">You can even write your own deps-new templates!</h1><p>AND YOU SHOULD!</p><p>(looking at you, framework authors!) <hr></p><p><a id="h:5FB73441-42F4-437E-AB47-D46755562B82"></a></p><h1 id="what_can_we_as_clojure_devs_do%3F">What can we as clojure devs do?</h1><ul><li>Does your favourite framework have a deps-new template? Add it!</li><li>Do you find yourself copying the same code everywhere? Try and extract it into a template!</li></ul><p><hr></p><p><a id="h:E21AA902-7FB4-47C7-BF16-E755FBF2E4B8"></a></p><h1 id="references_to_learn_deps-new">References to learn deps-new</h1><ul><li>What is deps-new? <a href='https://github.com/seancorfield/deps-new/blob/develop/README.md'>github/deps-new/README</a></li><li>Templates provided by the Community <a href='https://github.com/seancorfield/deps-new?tab=readme-ov-file#templates'>deps-new/README#templates</a></li><li>How to write your own template? <a href='https://practical.li/blog-staging/posts/create-deps-new-template-for-clojure-cli-projects/'>practical.li/create-deps-new-template-for-clojure-cli-projects</a></li></ul><p><hr></p><p><a id="h:28A24030-01E1-44EC-B1CA-5737CBF261B8"></a></p><h1 id="i_want_to_add_more_aliases_to_my_repository%21">I want to add more aliases to my repository!</h1><ul><li>How do I connect to a REPL?</li><li>How do I setup logging?</li><li>&#x2026;</li></ul><p><hr></p><p><a id="h:42B7608D-EE98-478B-88B9-A368F661775E"></a></p><h1 id="i_use_neil%21">I use neil!</h1><p><a href="https://github.com/babashka/neil">https://github.com/babashka/neil</a></p><ul><li>neil can add aliases for you!</li><li>neil can do so much more!</li></ul><p><hr></p><p><a id="h:BCDC7604-ECD0-47E9-A309-411E52C049E8"></a></p><h1 id="examples_of_using_neil%21">Examples of using neil!</h1><pre><code class="language-fish">neil add cider
</code></pre><pre><code class="language-clojure">:cider ;; added by neil
{:extra-deps {cider/cider-nrepl {:mvn/version &quot;0.47.0&quot;}
              djblue/portal {:mvn/version &quot;0.52.2&quot;}
              mx.cider/tools.deps.enrich-classpath {:mvn/version &quot;1.19.0&quot;}
              nrepl/nrepl {:mvn/version &quot;1.1.1&quot;}
              refactor-nrepl/refactor-nrepl {:mvn/version &quot;3.10.0&quot;}}
 :main-opts  &#91;&quot;-m&quot; &quot;nrepl.cmdline&quot;
              &quot;--middleware&quot; &quot;&#91;cider.nrepl/cider-middleware,refactor-nrepl.middleware/wrap-refactor,portal.nrepl/wrap-portal&#93;&quot;&#93;}
</code></pre><p><hr></p><p><a id="h:7B40948C-CF1D-4970-9FED-F7F1BBF873F8"></a></p><h1 id="neil_can_do_more_than_just_add_aliases%21">Neil can do more than just add aliases!</h1><pre><code class="language-fish">neil dep search next.jdbc
</code></pre><p>RESULTS:</p><pre><code>:lib pro.juxt.clojars-mirrors.com.github.seancorfield/next.jdbc :version &quot;1.2.674&quot; :description &quot;pro.juxt.clojars-mirrors.com.github.seancorfield/next.jdbc on Maven&quot;
:lib net.cloudopt.next/cloudopt-next-jdbc :version &quot;3.1.3.0-RELEASE&quot; :description &quot;net.cloudopt.next/cloudopt-next-jdbc on Maven&quot;
:lib com.github.seancorfield/next.jdbc :version &quot;1.3.999-SNAPSHOT&quot; :description &quot;The next generation of clojure.java.jdbc: a new low-level Clojure wrapper for JDBC-based access to databases.&quot;
:lib seancorfield/next.jdbc :version &quot;1.2.659&quot; :description &quot;The next generation of clojure.java.jdbc: a new low-level Clojure wrapper for JDBC-based access to databases.&quot;
:lib com.layerware/hugsql-adapter-next-jdbc :version &quot;0.5.3&quot; :description &quot;next.jdbc hugsql adapter&quot;
:lib dev.weavejester/ragtime.next-jdbc :version &quot;0.9.4&quot; :description &quot;Ragtime migrations for next.jdbc&quot;
:lib hugsql-next-jdbc/hugsql-next-jdbc :version &quot;0.1.3&quot; :description &quot;next.jdbc adapter for HugSQL&quot;
:lib de.active-group/active-jdbc :version &quot;0.2.1&quot; :description &quot;Functions to work with JDBC building upon next.jdbc.&quot;
:lib daaku/pgmig :version &quot;2.0.1&quot; :description &quot;Database migrations for PostgreSQL using next.jdbc.&quot;
:lib taskmaster/taskmaster :version &quot;0.0.4-SNAPSHOT-1&quot; :description &quot;Background publisher/consumer on top of Postgres, next.jdbc and hikari-cp&quot;
</code></pre><p><hr></p><p><a id="h:7254A3DD-3BB1-404B-9466-1230861DB4B2"></a></p><h1 id="neil_can_do_more_than_just_add_aliases%21">Neil can do more than just add aliases!</h1><pre><code class="language-fish">neil dep add :lib com.github.seancorfield/next.jdbc
</code></pre><pre><code class="language-fish">neil dep add :lib nextjournal/clerk :alias :cider
</code></pre><p><hr></p><p><a id="h:7E82661A-CA5D-4B9B-8901-66CD54731B6D"></a></p><h1 id="what_can_we_as_clojure_devs_do%3F">What can we as clojure devs do?</h1><ul><li>Make it easy to add aliases to neil!</li><li>I would like to add to existing aliases instead of creating new ones!</li></ul><p><hr></p><p><a id="h:48270D61-BBD2-4323-8ADE-63AAA3438069"></a></p><h1 id="references_for_learning_more_about_neil">References for learning more about neil</h1><ul><li>Using neil <a href='https://blog.michielborkent.nl/new-clojure-project-quickstart.html'>michielborkent/new-clojure-project-quickstart.html</a></li><li>All the options <a href='https://github.com/babashka/neil/blob/main/README.md'>github/babashka/neil/README.md</a></li><li>My neil script, with aliases I use <a href='https://github.com/vedang/neil'>github/vedang/neil</a></li></ul><p><hr></p><p><a id="h:EB7B3E7E-5DAE-4A7A-98FB-C0395C659503"></a></p><h1 id="okay%2C_i_am_now_ready_to_spin_up_a_repl_and_connect_to_it%21">Okay, I am now ready to spin up a REPL and connect to it!</h1><pre><code class="language-fish">clojure -M:test:logs-dev:cider
</code></pre><h2 id="speaker_notes">Speaker Notes</h2><p>Wait, what's the logging alias?</p><p>Logging is hard!</p><p>So once I figured it out, I made a template! <hr></p><p><a id="h:FE043DED-0F32-4B50-9955-0D8087A7BD7F"></a></p><h1 id="i%27m_happy_i_never_have_to_figure_this_out_again%21">I'm happy I never have to figure this out again!</h1><pre><code class="language-fish">neil add logs-dev
</code></pre><pre><code class="language-clojure">:logs-dev ;; added by neil
 {:extra-deps {me.vedang/logger {:local/root &quot;logger&quot;}}
  :jvm-opts
   &#91;&quot;-Dclojure.tools.logging.factory=clojure.tools.logging.impl/log4j2-factory&quot;
    &quot;-Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.Slf4jLog&quot;
    &quot;-Dlog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector&quot;
    &quot;-Dlog4j2.configurationFile=logger/log4j2-dev.xml&quot;
    ;; Change logging.level to one of TRACE, DEBUG, INFO, WARN, ERROR
    ;; depending on requirement during development
    &quot;-Dlogging.level=DEBUG&quot;&#93;}
</code></pre><p><hr></p><p><a id="h:A0A4E361-EDE2-4021-8A11-E83757D83F27"></a></p><h1 id="templates_for_the_win">Templates for the win</h1><pre><code class="language-fish">clojure -Sdeps '{:deps {io.github.vedang/clj-logging {:git/sha &quot;e009d366c827705f513ef9018ffd920a49ce19da&quot;}}}' -Tnew create :template me.vedang/logger :name me.vedang/logger
</code></pre><pre><code class="language-fish">tree logger
</code></pre><p>RESULTS:</p><pre><code>logger
├── README.org
├── deps.edn
├── project.clj
├── resources
│   └── logger
│       ├── log4j2-dev.xml
│       └── log4j2-prod.xml
└── src
    └── me
        └── vedang
            └── logger
                └── interface.clj

7 directories, 6 files
</code></pre><p><hr></p><p><a id="h:4939A4F2-597E-4349-B21A-3CB8F18902D4"></a></p><h1 id="i_use_structured_logging_everywhere%21">I use structured logging everywhere!</h1><p><a href="http://pedestal.io/pedestal/0.7/reference/logging.html">http://pedestal.io/pedestal/0.7/reference/logging.html</a></p><pre><code class="language-clojure">&#40;in-ns 'me.vedang.depsnew-lib&#41;
&#40;require '&#91;me.vedang.logger.interface :as log&#93;&#41;
&#40;log/info :function :welcome
          :action :starting
          :args {:greeting &quot;Hello World&quot;}&#41;
</code></pre><pre><code class="language-js">{
  &quot;instant&quot;: {
    &quot;epochSecond&quot;: 1711161317,
    &quot;nanoOfSecond&quot;: 787564000
  },
  &quot;thread&quot;: &quot;nREPL-session-d6c4a5db-e57c-4efe-906b-7e5f1d0ad6e6&quot;,
  &quot;level&quot;: &quot;INFO&quot;,
  &quot;loggerName&quot;: &quot;me.vedang.depsnew-lib&quot;,
  &quot;message&quot;: &quot;{\&quot;function\&quot;:\&quot;welcome\&quot;,\&quot;action\&quot;:\&quot;starting\&quot;,\&quot;args\&quot;:{\&quot;greeting\&quot;:\&quot;Hello World\&quot;},\&quot;line\&quot;:3}&quot;,
  &quot;endOfBatch&quot;: true,
  &quot;loggerFqcn&quot;: &quot;org.apache.logging.slf4j.Log4jLogger&quot;,
  &quot;contextMap&quot;: {},
  &quot;threadId&quot;: 41,
  &quot;threadPriority&quot;: 5
}
</code></pre><p>Prefer emitting logs as JSON events. This lets us push them to Observability tools easily in production. <hr></p><p><a id="h:9E2199EF-9BD7-437B-B0F7-B1380A269827"></a></p><h1 id="i_want_beautiful_documentation">I want beautiful Documentation</h1><p>Clojure is REPL-first! Where are my notebooks?</p><h2 id="speaker_notes">Speaker Notes</h2><p>We've been writing notebooks and refactoring them into usable code-bases all our lives. So why do we not have beautiful looking notebooks like the Python people?</p><p><hr></p><p><a id="h:4D700447-D8AA-4A4E-A24F-3CEC66204E52"></a></p><h1 id="i_use_clerk%3A_write_beautiful_notebooks_%3Cspan_class%3D%22underline%22%3Eand%3C%2Fspan%3E_beautiful_code-bases%21">I use Clerk: write beautiful notebooks <span class="underline">and</span> beautiful code-bases!</h1><p><a href="https://github.com/nextjournal/clerk">https://github.com/nextjournal/clerk</a></p><p>Write your comments like you write Markdown, done!</p><p><hr></p><p><a id="h:40958F52-414B-43E1-A87D-A21F8563EB2B"></a></p><h1 id="clerk%3A_write_beautiful_notebooks_%3Cspan_class%3D%22underline%22%3Eand%3C%2Fspan%3E_beautiful_code-bases%21">Clerk: write beautiful notebooks <span class="underline">and</span> beautiful code-bases!</h1><pre><code class="language-clojure">;;; # 📈 Sunday Morning Fun With Our Group Meditation Data

;; First, we need to parse the data from the Whatsapp Group. Whatsapp
;; provides a `export-chat` feature that we can use, but there are
;; some things we need to fix first:

;; ##  Whatsapp uses a ridiculous format for the date when the message was sent
&#94;{:nextjournal.clerk/visibility {:code :show :result :hide}}
&#40;def whatsapp-export-date-format
  &quot;Frankly, this is a ridiculous format that no one should use.&quot;
  &quot;MM/dd/yy, hh:mm a&quot;&#41;

;; The problem isn't so much the format, as the fact that they make it
;; hard to parse. For example, they won't export the date as
;; `&quot;05/01/23, 03:31 pm&quot;`. They will export it as `&quot;5/1/23, 3:31 PM&quot;`.
;; Every part of this is meant to fail your parser. 🤷🏽‍♂️
</code></pre><p>What Clerk notebooks look like:</p><ul><li><a href="https://recife.pfeodrippe.com/notebooks/recife/notebook/slow_start.html">https://recife.pfeodrippe.com/notebooks/recife/notebook/slow_start.html</a></li><li><a href="https://book.clerk.vision/#vega-lite">https://book.clerk.vision/#vega-lite</a></li></ul><h2 id="speaker_notes">Speaker Notes</h2><ul><li>Talk about Separedit!</li></ul><p><hr></p><p><a id="h:CA787ACA-39D7-485C-B3FC-C3087B32F2F1"></a></p><h1 id="i_use_tagref%3A_clear_separation_of_code_and_documentation">I use tagref: clear separation of code and documentation</h1><p><a href="https://github.com/stepchowfun/tagref">https://github.com/stepchowfun/tagref</a></p><pre><code class="language-clojure">&#40;require 'clojure.math&#41;

;; &#91;tag:polynomial&#95;nonzero&#93; This function never returns zero.
&#40;defn polynomial &#91;x&#93;
  &#40;+ &#40;clojure.math/pow x 2&#41; 1&#41;&#41;

;;; in some other file
&#40;defn inverse-polynomial &#91;x&#93;
  ;; This is safe due to &#91;ref:polynomial&#95;nonzero&#93;.
  &#40;/ 1 &#40;polynomial x&#41;&#41;&#41;
</code></pre><p><hr></p><p><a id="h:E432FDCF-210A-48E2-8946-882477385755"></a></p><h1 id="documentation_for_the_team%3A_tagref">Documentation for the team: tagref</h1><pre><code class="language-fish">tagref list-tags | awk '{print $1}'
</code></pre><p>RESULTS:</p><pre><code>&#91;tag:gifs&#95;need&#95;to&#95;be&#95;converted&#95;to&#95;video&#95;format&#93;
&#91;tag:rules&#95;for&#95;deciding&#95;when&#95;we&#95;should&#95;update&#95;a&#95;concept&#95;record&#93;
&#91;tag:an&#95;important&#95;thing&#95;to&#95;understand&#95;about&#95;repetition&#95;records&#93;
&#91;tag:rules&#95;for&#95;deciding&#95;when&#95;we&#95;should&#95;update&#95;a&#95;chapter&#95;record&#93;
&#91;tag:rules&#95;to&#95;mark&#95;a&#95;chapter&#95;as&#95;done&#95;for&#95;now&#93;
&#91;tag:edge&#95;case&#95;empty&#95;concepts&#95;when&#95;updating&#95;chapter&#95;or&#95;concept&#95;records&#93;
&#91;tag:the&#95;order&#95;in&#95;which&#95;parsers/checkers&#95;run&#95;is&#95;important&#93;
&#91;tag:next-question&#95;is&#95;the&#95;heart&#95;of&#95;our&#95;logic&#93;
&#91;tag:rules&#95;for&#95;picking&#95;the&#95;next&#95;concept&#95;for&#95;this&#95;student&#93;
&#91;tag:rules&#95;for&#95;picking&#95;the&#95;next&#95;chapter&#95;for&#95;this&#95;student&#93;
&#91;tag:field&#95;name&#95;change&#95;needs&#95;to&#95;be&#95;the&#95;last&#95;parser&#93;
&#91;tag:no&#95;need&#95;to&#95;optimise&#95;insertion&#95;db&#95;calls&#93;
&#91;tag:clojure.string/replace&#95;using&#95;replacement&#95;function&#93;
&#91;tag:what&#95;can&#95;session&#95;filters&#95;contain?&#93;
&#91;tag:rules&#95;to&#95;mark&#95;a&#95;concept&#95;as&#95;done&#95;for&#95;now&#93;
&#91;tag:extend&#95;protocols&#95;for&#95;automatic&#95;conversion&#95;of&#95;data&#95;to&#95;and&#95;from&#95;pg&#93;
&#91;tag:meaningful&#95;ring&#95;responses&#93;
&#91;tag:what&#95;to&#95;keep&#95;in&#95;mind&#95;when&#95;updating&#95;routes&#93;
&#91;tag:on&#95;magic&#95;links&#93;
</code></pre><p><hr></p><p><a id="h:29C1F422-B93E-4347-ABEE-980B0D805130"></a></p><h1 id="references_for_improving_documentation">References for improving documentation</h1><ul><li>Using Clerk <a href='https://book.clerk.vision/'>book.clerk.vision</a></li><li>Using Separedit <a href='https://github.com/twlz0ne/separedit.el'>github/twlz0ne/separedit.el</a></li><li>Using Tagref <a href='https://github.com/stepchowfun/tagref'>github/stepchowfun/tagref</a></li><li>Jack Rusher on the power of interactive notebooks (among other things): <a href='https://www.youtube.com/watch?v=8Ab3ArE8W3s'>Stop Writing Dead Programs</a></li></ul><p><hr></p><p><a id="h:52C3A859-91F2-4E59-ADE9-E2B648BF1C0C"></a></p><h1 id="i_want_to_write_clean%2C_maintainable_code%21">I want to write clean, maintainable code!</h1><p><a href="https://github.com/clj-kondo/clj-kondo">https://github.com/clj-kondo/clj-kondo</a> <a href="https://github.com/weavejester/cljfmt">https://github.com/weavejester/cljfmt</a> <a href="https://github.com/kkinnear/zprint">https://github.com/kkinnear/zprint</a></p><p>I do automatic Linting and Formatting on the code base!</p><p><hr></p><p><a id="h:5ED92E91-266F-47C4-B6B6-6F2EFBD0FB92"></a></p><h1 id="clj-kondo%3A_my_recommendation">clj-kondo: My recommendation</h1><p><strong>Export clj-kondo config</strong> for libraries you use! (<em>create <code>.clj-kondo</code> directory at root first</em>)</p><pre><code class="language-fish">clj-kondo --lint &quot;$&#40;clojure -A:dev:test:cider:build -Spath&#41;&quot; --copy-configs --skip-lint
</code></pre><p>(<code>clojure-lsp</code> will do this automatically)</p><pre><code class="language-fish">tree .clj-kondo
</code></pre><p>RESULTS:</p><pre><code>.clj-kondo
├── babashka
│   └── fs
│       └── config.edn
├── http-kit
│   └── http-kit
│       ├── config.edn
│       └── httpkit
│           └── with&#95;channel.clj
├── nextjournal
│   └── clerk
│       ├── config.edn
│       └── nextjournal
│           └── clerk
│               └── viewer.clj&#95;kondo
└── rewrite-clj
    └── rewrite-clj
        └── config.edn

12 directories, 6 files
</code></pre><p><hr></p><p><a id="h:455C7D9C-E790-40B7-8D12-9D4A880DA807"></a></p><h1 id="clj-kondo%3A_my_recommendation">clj-kondo: My recommendation</h1><p>Commit your <code>.clj-kondo</code> folder!</p><p><hr></p><p><a id="h:C2FB456F-F43E-4D2D-8FDE-01D1254039F0"></a></p><h1 id="cljfmt%3A_my_recommendation">cljfmt: My recommendation</h1><p>(For Emacsen) Use Apheleia!</p><p>Runs code-formatter on the buffer "at the right time" with minimal disturbance.</p><p><hr></p><p><a id="h:ABB58979-B294-4E02-AFC4-37DA90646DB4"></a></p><h1 id="what_can_we_as_clojure_devs_do%3F">What can we as clojure devs do?</h1><p>Add <code>clj-kondo</code> configuration for your favourite libraries!</p><p><hr></p><p><a id="h:091458FA-32EC-40E6-AC1E-A30687EC5C8F"></a></p><h1 id="references_for_clj-kondo%2C_cljfmt_and_zprint">References for clj-kondo, cljfmt and zprint</h1><ul><li>clj-kondo Documentation. <a href='https://github.com/clj-kondo/clj-kondo'>github/clj-kondo/clj-kondo</a></li><li>cljfmt Documentation. <a href='https://github.com/weavejester/cljfmt'>github/weavejester/cljfmt</a></li><li>The zprint Reference. <a href='https://github.com/kkinnear/zprint/blob/main/doc/reference.md'>github/kkinnear/zprint/doc/reference.md</a></li><li>Apheleia for Emacs. <a href='https://github.com/radian-software/apheleia'>github/radian-software/apheleia</a></li></ul><p><hr></p><p><a id="h:238F58ED-D45D-4468-A6B6-01A4BD38268B"></a></p><h1 id="can_we_improve_the_repl_experience%3F">Can we improve the REPL experience?</h1><p>After all, this is where we are all day!</p><h2 id="speaker_notes">Speaker Notes</h2><p>IF we can make the experience even 1% better, isn't that something amazing?</p><p><hr></p><p><a id="h:B8F12EBF-1B4B-422E-A07E-FA87F9D9B805"></a></p><h1 id="debugging_like_i%27ve_never_experienced_before%3A_flowstorm%21">Debugging like I've never experienced before: Flowstorm!</h1><pre><code class="language-fish">neil add cider-storm
</code></pre><pre><code class="language-clojure">:cider-storm ;; added by neil
 {:classpath-overrides
  ;; we need to disable the official compiler and use ClojureStorm
  {org.clojure/clojure nil}
  :extra-deps {cider/cider-nrepl {:mvn/version &quot;0.47.0&quot;}
               com.github.flow-storm/clojure {:mvn/version &quot;1.11.2&quot;}
               com.github.flow-storm/flow-storm-dbg {:mvn/version &quot;3.13.1&quot;}
               djblue/portal {:mvn/version &quot;0.52.2&quot;}
               mx.cider/tools.deps.enrich-classpath {:mvn/version &quot;1.19.0&quot;}
               nrepl/nrepl {:mvn/version &quot;1.1.1&quot;}
               org.openjfx/javafx-controls {:mvn/version &quot;23-ea+3&quot;}
               org.openjfx/javafx-base {:mvn/version &quot;23-ea+3&quot;}
               org.openjfx/javafx-graphics {:mvn/version &quot;23-ea+3&quot;}
               org.openjfx/javafx-swing {:mvn/version &quot;23-ea+3&quot;}
               refactor-nrepl/refactor-nrepl {:mvn/version &quot;3.10.0&quot;}}
  :main-opts  &#91;&quot;-m&quot; &quot;nrepl.cmdline&quot;
               &quot;--middleware&quot; &quot;&#91;flow-storm.nrepl.middleware/wrap-flow-storm,cider.nrepl/cider-middleware,refactor-nrepl.middleware/wrap-refactor,portal.nrepl/wrap-portal&#93;&quot;&#93;
  :jvm-opts &#91;&quot;-Dclojure.storm.instrumentEnable=true&quot;
             &quot;-Dclojure.storm.instrumentOnlyPrefixes=me.vedang.&quot;&#93;}
</code></pre><p><hr></p><p><a id="h:CAFE6CF8-C675-4D48-9B0A-C123AF6D4DB5"></a></p><h1 id="debugging_with_flowstorm">Debugging with Flowstorm</h1><pre><code class="language-clojure">&#40;in-ns 'me.vedang.depsnew-lib&#41;

&#40;defn foo &#91;n&#93;
  &#40;-&gt;&gt; &#40;range n&#41;
       &#40;filter odd?&#41;
       &#40;partition-all 2&#41;
       &#40;map second&#41;
       &#40;drop 10&#41;
       &#40;reduce +&#41;&#41;&#41;
</code></pre><p><hr></p><p><a id="h:6EF62F47-7E2E-410C-8EDF-A64A51D843E9"></a></p><h1 id="references_for_learning_more_about_flowstorm">References for learning more about Flowstorm</h1><ul><li><a href='http://www.flow-storm.org/'>Flowstorm website</a></li><li>@jpmonetta showing a full walk-through <a href='https://www.youtube.com/watch?v=BuSpMvVU7j4'>Reifying execution, the interactive programming missing piece</a></li></ul><p><hr></p><p><a id="h:86DBFE60-028E-44D0-A2C8-8B7C18101E89"></a></p><h1 id="why_micro-services%3F">Why Micro-services?</h1><p><strong>Smaller teams, clear ownership!</strong></p><p><hr></p><p><a id="h:43403155-E5C8-4D73-8B82-8D68294CD27A"></a></p><h1 id="why_micro-services%3F">Why Micro-services?</h1><ul><li>Easier to test</li><li>Clear contracts (API)</li><li>Independent deployments</li></ul><p><hr></p><p><a id="h:3FAC1EA3-ADE4-4912-855B-8EDE6FF65EF7"></a></p><h1 id="why_monorepos%3F">Why Monorepos?</h1><ul><li>No version hell</li><li>Easy refactoring across multiple services (in theory)</li><li>Easy collaboration and on-boarding (in theory)</li></ul><p><hr></p><p><a id="h:739109BD-0AC6-4343-A103-AA9021FF99EA"></a></p><h1 id="so_what_goes_wrong_with_monorepos%3F">So what goes wrong with Monorepos?</h1><ul><li>Contracts are unwritten</li><li>Testing becomes messy (specifically, test-suite time)</li><li>Refactoring, Collaboration, On-boarding Hell</li></ul><h2 id="speaker_notes">Speaker Notes</h2><p>Eventually, <strong>teams start building conventions and tooling</strong> and start refactoring.</p><p>The problem is, it's too late by now.</p><p><hr></p><p><a id="h:332F0BC1-BA71-4076-B69D-CD2C9BA95906"></a></p><h1 id="i_use_polylith_from_day_one">I use Polylith from day one</h1><p><a href="https://github.com/polyfy/polylith">https://github.com/polyfy/polylith</a></p><p><strong>Polylith</strong> is a set of conventions and tooling that has thought through all these problems!</p><p><hr></p><p><a id="h:7072163A-F7AB-41BC-9DC7-E831081E3500"></a></p><h1 id="let%27s_create_our_polylith_monorepo%21">Let's create our Polylith monorepo!</h1><pre><code class="language-fish">poly create workspace name:polylith top-ns:me.vedang :commit
</code></pre><pre><code class="language-fish">tree -L 3
</code></pre><p>RESULTS:</p><pre><code>.
├── bases
├── components
├── deps.edn
├── development
│   └── src
├── logo.png
├── projects
├── readme.md
└── workspace.edn

6 directories, 4 files
</code></pre><h2 id="speaker_notes">Speaker Notes</h2><p>Addendum: you can also create a workspace in an existing repo: <code>poly create workspace top-ns:me.vedang</code></p><p><hr></p><p><a id="h:ABCDF299-13C8-4091-AB6B-CBFC852B3497"></a></p><h1 id="the_terminology_of_polylith%3A_project">The terminology of Polylith: project</h1><p><strong>projects/<name></strong> : Interface with hosting platform</p><p>Let's think through an example:</p><ol><li>Static Site : <strong>projects/weblog<sub>static</sub></strong> (Artifact: Public Folder, Deployment: Github Pages)</li><li>Dynamic Site : <strong>projects/weblog<sub>dynamic</sub></strong> (Artifact: Uberjar, Deployment: Dockerfile, fly.toml)</li></ol><p><hr></p><p><a id="h:56735D24-1D67-4B39-952C-22BF2B6E2229"></a></p><h1 id="the_terminology_of_polylith%3A_base">The terminology of Polylith: base</h1><p><strong>bases/<name></strong> : Interface with the external world</p><p>Continuing with our example:</p><ol><li>Static Site: <strong>bases/server<sub>static</sub></strong></li><li>Dynamic Site: <strong>bases/server<sub>dynamic</sub></strong> (server! routes)</li></ol><h2 id="speaker_notes">Speaker Notes</h2><p>This folder contains code that interacts with the external world</p><p>Think: CLI tool, Server, Lambda function, Worker</p><p><hr></p><p><a id="h:EC537EEA-4435-4FB2-B6F4-9D9D883AF645"></a></p><h1 id="the_terminology_of_polylith%3A_component">The terminology of Polylith: component</h1><p><strong>components/<name></strong> : Interface with a module of business logic.</p><p>Continuing with our example:</p><ol><li><strong>components/content</strong>: The markdown files I want to serve as posts</li><li><strong>components/render</strong> : Code to convert MD files to HTML</li><li><strong>components/readers</strong>: Logged-in visitors on my dynamic website</li></ol><h2 id="speaker_notes">Speaker Notes</h2><p>This folder contains re-usable, modular code that implements specific functionality.</p><p><hr></p><p><a id="h:4BE83BAC-62C4-4B1E-A4E8-F58F786BD0E7"></a></p><h1 id="how_does_all_this_tie_together%3F">How does all this tie together?</h1><pre><code>projects/weblog&#95;static -&gt; &#91;bases/server&#95;static
                           components/content
                           components/render&#93;

projects/weblog&#95;dynamic -&gt; &#91;bases/server&#95;dynamic
                            components/content
                            components/render
                            components/readers&#93;
</code></pre><p><hr></p><p><a id="h:464073B8-D6A3-427D-BED4-18A95B5872A1"></a></p><h1 id="how_polylith_promotes_modular_architecture">How Polylith promotes Modular architecture</h1><p>Code outside a component can only refer to functions in the component's <code>interface</code> namespace.</p><h2 id="speaker_notes">Speaker Notes</h2><p>During development, you have access to the entire code-base! But there are rules around how you can access any code outside your own brick.</p><p>(so I don't need to know how you implement the code, I'm only concerned with the interface of your component)</p><p>Interface functions pass-through the arguments to appropriate internal functions.</p><p><hr></p><p><a id="h:E41E8FC5-2F54-4166-97B2-C8A019A7B320"></a></p><h1 id="polylith%3A_getting_the_best_of_micro-services_and_mono-repos">Polylith: getting the best of micro-services and mono-repos</h1><ul><li>Where does the Dockerfile go? <code>projects/weblog&#95;dynamic</code></li><li>Where do I put end-to-end tests? <code>projects/weblog&#95;dynamic/test</code></li><li>Where do I define the routes and the main class? <code>bases/server&#95;dynamic/core.clj</code></li><li>Which functions do I use to render HTML pages? <code>components/render/interface.clj</code></li><li>Which functions do I write contract tests for? <code>components/render/interface.clj</code></li><li>Which functions do I document thoroughly? <code>components/render/interface.clj</code></li><li>Where do I hook observability? <code>components/render/interface.clj</code>, <code>bases/server&#95;dynamic/core.clj</code></li><li>Where does the actual rendering functionality live? <code>render/posts.clj</code>, <code>render/tags.clj</code>, <code>render/index.clj</code> &#x2026;</li></ul><p><hr></p><p><a id="h:7BA51976-ED7F-40E3-87F3-015E96B77351"></a></p><h1 id="what_does_the_poly_tool_give_me%3F">What does the poly tool give me?</h1><ul><li>Enables running the right tests</li><li>Checks to ensure component code is used properly everywhere</li><li>Upgrades libraries across all your bricks</li></ul><p>&#x2026; and much more</p><p><hr></p><p><a id="h:D4FED61E-AAEA-411B-A7EA-2D9D186C7BC0"></a></p><h1 id="references_for_learning_more_about_polylith">References for learning more about Polylith</h1><ul><li>Learn Polylith Conventions <a href='https://polylith.gitbook.io/polylith'>polylith.gitbook.io</a></li><li>Use <code>poly</code> to enforce conventions: <a href='https://cljdoc.org/d/polylith/clj-poly/CURRENT/doc/readme'>cljdoc.org/polylith/clj-poly</a></li><li>Sean Corfield's series on monorepos: <a href='https://corfield.org/blog/2021/02/23/deps-edn-monorepo/'>corfield.org/deps-edn-monorepo</a></li></ul><p><hr></p><p><a id="h:61FC33B2-E5AA-4DCC-A8A3-6C227E0FBA4B"></a></p><h1 id="there_is_so_much_more_%26%23x2026%3B">There is so much more &#x2026;</h1><ul><li>CI/CD! (Use <code>babashka</code>!)</li><li>Observability (<code>pedestal</code>! or <code>iapetos</code> + <code>clj-otel</code>)</li><li>Web Development (<code>martian</code> makes talking to APIs a breeze!)</li></ul><p><hr></p><p><a id="h:1838A668-A2BF-4B46-BA27-6A4CC1C1ECAA"></a></p><h1 id="come_say_hi%21">Come say hi!</h1><ul><li>@vedang on 🐘fosstodon, 🖥️github, 🐦twitter</li><li>https://unravel.tech<ul><li>We can help with your problems!</li><li>(design, product, engineering)</li></ul></li></ul><p><em>THANK YOU! QUESTIONS?</em></p>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/great-books-by-indian-authors-my-recommendations-to-arati/</id>
    <link href="https://vedang.me/great-books-by-indian-authors-my-recommendations-to-arati/"/>
    <title>Great Books by Indian Authors, My Recommendations to Arati</title>
    <updated>2024-01-16T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>Arati wrote to me today on Whatsapp:</p><blockquote><p> Hi Vedang, needed some advice from you. </p><p> My cousin brother is visting India and is married to an Amercian woman. She likes to read fiction, anything other than too much gore and murder is alright. She is keen on trying out some Indian authored books in the fiction space. Anything you recommend? </p><p> I'm guessing nothing with myth and mythology since she is not aware of the basics and maybe something that isn't too India context based? What do you have in mind? </p></blockquote><p>This is a list I created based on that question.<sup><a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink">1</a></sup></p><ol><li><strong>Cuckold, by Kiran Nagarkar</strong> [Historical Fiction]: One of my favourite books! This book is about Mirabai's obsession with Krishna, from the point of view of her husband the Yuvraj of Mewar. It is incredible, fast-paced historical fiction set in 16th century India, and it reads like a thriller. (Cuckold: <a href="https://amzn.eu/d/6Tt6dFr">https://amzn.eu/d/6Tt6dFr</a>)</li><li><strong>The Ibis Trilogy, by Amitav Ghosh</strong> [Historical Fiction]: The story is set around the Opium Wars, via which Britain established absolute dominance over China. It is a great book about colonialism, how the British exploited India and China as their opium factories, and what that led to. The cast of characters is hilarious and very Indian. She should start from the first book (Sea of Poppies) and see if she likes the flavour of the writing. (Sea of Poppies: <a href="https://amzn.eu/d/cnyYtQj">https://amzn.eu/d/cnyYtQj</a>)</li><li><strong>Malgudi Days, R.K. Narayan</strong> [Short Stories]: Malgudi Days is a classic for a reason. It is beautiful writing on village life, and Swami's adventures with his friends are a joy to read. (Malgudi Days: <a href="https://amzn.eu/d/h4jbb1k">https://amzn.eu/d/h4jbb1k</a>)</li><li><strong>No Presents Please, Jayant Kaikini</strong> [Short Stories, Translations]: A collection of short stories all set in Mumbai, all exploring the life of people living in Mumbai in different circumstances. Great writing! (No Presents Please: <a href="https://amzn.eu/d/9HxyhiN">https://amzn.eu/d/9HxyhiN</a>)</li><li><strong>One Part Woman, Perumal Murugan</strong> [Translations]: Incredible story about Kali and Ponna, a couple who live in a Tamilnadu village. They are deeply in love with each other, but unable to conceive. The families on both sides start getting involved and the "problem" starts taking over everything. Beautifully written and extremely poignant. The translation was so good, I wonder how much better the original in Tamil would be. (One Part Woman: <a href="https://amzn.eu/d/2SHfxFp">https://amzn.eu/d/2SHfxFp</a>)</li><li><strong>Aiwa Maru, Anant Samant</strong> [Translations]: Super steamy book about a sailor who is undertaking a perilous journey on an old, creaky ship. He falls in love with his friend's wife who is also travelling on this ship. It sticks with me because this kind of writing was shocking, I didn't expect it from an Indian author :P. The writing is wonderful! (Aiwa Maru: <a href="https://amzn.eu/d/efNZDFi">https://amzn.eu/d/efNZDFi</a>)</li><li>[Bonus] <strong>The Second Turn, MT Vasudevan Nair</strong> [Mahabharata, Translations]: One of the most celebrated Mahabharata books is Randaamoozham, written in Malayalam by MTV. It tells us the story from Bhima's point of view. Luckily for us English Readers, Prem Panicker "recreated" it into English and that translation is freely available for everyone to read. If she is interested in the Mahabharata, ask her to put aside whatever book she brought and start with this one :). I'm attaching the PDF to this email.</li></ol><h2 id="footnotes">Footnotes</h2><p><sup><a id="fn.1" class="footnum" href="#fnr.1">1</a></sup> I asked some follow-up questions to get a better read (khi khi):</p><ol><li>What about Historical Fiction? Would she be interested in a book like ‘Murder at the Mushaira’? There are some great books in historical fiction, which she will be able to enjoy without the need for context.</li><li>⁠Let’s assume mythology is out for the moment, but is she interested in maybe reading about it? I can think of some short story collections that might be good.</li><li>⁠Which brings us to Short Stories. There are many incredible short story collections! Would she enjoy those?</li><li>⁠How about "cityscapes" — books which describe the life of a particular city at a particular time?</li><li>⁠Finally, some of the best books are english translations of regional language classics. These would be classic fiction books — love, romance, betrayal etc.</li></ol>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/my-answer-to-how-do-i-get-started-with-emacs/</id>
    <link href="https://vedang.me/my-answer-to-how-do-i-get-started-with-emacs/"/>
    <title>My answer to "How do I get started with Emacs with minimal config"</title>
    <updated>2023-08-29T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<h1 id="op%27s_requirements%3A">OP's Requirements:</h1><p>Features I want (have no issue trying to add manually, but would like to see it there) -</p><ul><li>Project Management</li><li>LSP with Linting</li><li>Popup/Toggle-based Terminals</li></ul><h1 id="my_answer%3A">My Answer:</h1><p>I'd be happy to explain / work with you on these requirements. What you are looking for:</p><ol><li><code>project.el</code> : Understanding what a project is (actions like searching in projects, switching projects, opening files in projects etc). IF you mean the other project management (task management) then <code>org-mode</code> which is a beast of it's own. Both are built-ins.</li><li><code>eglot</code> for LSP. Invoke with <code>M-x eglot</code> from inside whichever project.</li><li>I did not understand what this means.</li></ol><p>A long time back, I wrote some documents that you might find useful:</p><ol><li><a href="denote:20161111T202810.html" class="internal-link">What Your Programming Editor Should Be Able To Do</a> : a list of table-stakes operations, and key-bindings for my setup of Emacs.</li><li>An Opinionated Emacs Tutorial: Getting started fast. : A presentation I used to give in my previous work-place for getting started with Emacs: <a href="https://github.com/vedang/emacs-up/blob/master/docs/01_opinionated_emacs_tutorial.org">https://github.com/vedang/emacs-up/blob/master/docs/01_opinionated_emacs_tutorial.org</a> (This is more philosophy than quick-tips, but might be useful to you)</li></ol>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/sights-sounds-tastes-vibes-of-nyc/</id>
    <link href="https://vedang.me/sights-sounds-tastes-vibes-of-nyc/"/>
    <title>Sights, sounds, tastes, vibes of New York City, my recommendations to Kapil and Payal</title>
    <updated>2023-08-18T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>Hello Kapil and Payal!</p><p>I hope you have a wonderful time in New York. I'd promised Kapil that I'd send him a list of my recommendations, so here they are:</p><h1 id="great_walks_to_build_up_an_appetite%21">Great Walks to build up an appetite!</h1><p>NYC is amazing for walking. Here are my suggestions (starting from the best):</p><ol><li>Walk on Broadway, starting from St Marks Plaza all the way to Central Park (as much as you can): We start the walk for shopping + food, and then continue walking because the architecture becomes gorgeous.</li><li>Walk from South Cove Park along the Hudson River all the way down to Battery Park (Statue of Liberty Vista Point): You walk through a gorgeous park, greenery on one side and the river on the other. Great for Romance.</li><li>Cross the Brooklyn Bridge from Manhattan to Brooklyn, and then walk the Brooklyn Promenade: This is similar to above, with the added advantage that there are excellent restaurants within walking distance of the Promenade. If you do this, try Juliana's Pizza (and not the more famous Grimaldi's next to it) when you cross Brooklyn bridge, and then explore DUMBO if you are interested in architecture, or go the other direction if you want a gorgeous night-time walk experience (the lights are glorious)</li><li>The High Line: Very touristy, so possibly super crowded. But if you are lucky and don't get too much of a crowd, it's totally worth it. The art along the walk is gorgeous. Start at the North end and head down to Chelsea Market, where you can have the second best Tacos in New York at 'Los Tacos No. 1'</li></ol><h1 id="food%21">Food!</h1><ol><li>Reunion (Israeli Food): It's very difficult to pick a "best" but this is what pops up in my head when I think of my favourite food experience.</li><li>Birria-Landia: Best Tacos I've tasted in my life. This is a small road-side stall in Brooklyn. But don't be fooled, it's the absolute best Mexican food I ate.</li></ol><h1 id="drinks%21">Drinks!</h1><p>My favourite bars, pubs, lounges, in no particular order:</p><ol><li>Stickett Inn : Cider bar with the best cocktails I've had so far (their star drink is called Stickett Inn Cider <em>stick it inside her</em>)</li><li>Bathtub Gin : Beautiful speakeasy, jazz and burlesque performances</li><li>Gallow Green : The rooftop restaurant of McKittrick Hotel. Everyone is so fancy here :D. When you go for 'Sleep no more' check this out as well</li></ol><h1 id="ice_cream%21">Ice Cream!</h1><p>New York is famous for ice-cream. Honestly, I think we do it better in India. They don't have nuts and raisins, they don't have wonderful crunchy mixed flavors. But, here are the recommendations (starting from the best):</p><ol><li>Amorino's</li><li>Van Leeuwen's</li><li>Big Gay Ice Cream</li></ol><h1 id="parks%21">Parks!</h1><p>Apart from the famous Central Park, try and hang out at:</p><ol><li>Byrant Park and Union Square Park: The markets around these parks is super fun, there are lots of temporary stalls for different kinds of food and trinkets.</li><li>Tomkins Square Park: Is a fun park to read a book in, and the super famous Tomkins Square Bagels are a must try.</li></ol><h1 id="absolutely_don%27t_miss">Absolutely Don't Miss</h1><ol><li>Central Park: You've heard about this from everyone. It's absolutely amazing. When I go back to New York, my first sunny day is always going to be in Central Park. See if you can attend a 'Shakespeare in the Park' play!</li><li>Noguchi Museum: The MET is amazing, but if you want to see a glorious use of space, check out Noguchi. The set designer in you will be pleased!</li></ol><h1 id="sites_i_used_to_keep_track_of_the_latest_events_when_i_was_in_nyc%3A">Sites I used to keep track of the latest events when I was in NYC:</h1><ul><li>The <a href='https://www.theskint.com/'>skint</a></li><li><a href='https://www.timeout.com/newyork/things-to-do'>TimeOut NYC</a></li><li><a href='https://www.atlasobscura.com/things-to-do/new-york'>Atlas Obscura</a> (hidden gems!)</li></ul>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/notes-on-the-why-and-how-of-taking-notes/</id>
    <link href="https://vedang.me/notes-on-the-why-and-how-of-taking-notes/"/>
    <title>Notes on the why and how of taking notes</title>
    <updated>2023-08-11T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><em>I take notes on everything, and I love the meta subject of "how to take notes". The slides below are from a small presentation I did to quickly explain the ethos of note-taking and why I think it's such a powerful habit.</em></p><p><em>Please note: this was meant to be a 30 minute talk, and it was meant to answer specific questions that the audience had read. I am publishing these slides as a reference for the audience at that talk. If you are interested in the topic of note-taking, you'll definitely find something useful here. If you don't understand something here (or better still, violently disagree with it), please write to me! I'd love to learn from you!</em></p><p><div class="orgtoc"></p><ul><li><a href='#orgc3fae8c'>Disclaimer: This is my experience</a></li><li><a href='#org1b06618'>Disclaimer: On taking notes (and on learning in general)</a></li><li><a href='#org0da9444'>Q1 svs: What are the Long-term Benefits of note-taking, if any?</a></li><li><a href='#orgdfc2b8c'>Q2 mayur: What's the line between taking notes and taking action?</a></li><li><a href='#orgdcf6c10'>Q3 joshua: How do you actually take good notes that make sense?</a></li><li><a href='#orgb89ca4f'>Q4 joshua: How do I reduce the number of notes I'm taking? I'm not aware of what's in my second brain.</a></li><li><a href='#orgaa7b179'>Fleeting Notes: Notes that are not "in my system"</a></li><li><a href='#org043b12f'>Any note I write without the luxury of pausing has no long-term benefit.</a></li><li><a href='#org35ba846'>"We think much faster than we speak"</a></li><li><a href='#org31cf907'>Reference Notes: Notes from books and videos</a></li><li><a href='#orgf78bfc0'>Because they are an index to a reference</a></li><li><a href='#org2afa581'>When taking reference notes, don't summarise!</a></li><li><a href='#org7c585eb'>You don't need to take notes of everything</a></li><li><a href='#org290709d'>Writing notes and Reading notes are separate activities</a></li><li><a href='#org2dedfa9'>Titles and tags are meant for searching</a></li><li><a href='#orgea661c8'>Links are meant to help you discover interesting trails</a></li><li><a href='#org43f7fcd'>Those were also examples of main notes</a></li><li><a href='#orgc43bc43'>"There are those who talk about productivity, and then there are those who are productive"</a></li><li><a href='#orgca677c0'>Takeaway: Use full sentences as titles for your notes.</a></li><li><a href='#org9962e02'>Takeaway: Avoid repetition in title+tag.</a></li><li><a href='#orgeebc7b7'>Takeaway: Write notes with an outcome in mind!</a></li><li><a href='#orge053760'>Takeaway: Make notes <strong>afterwards</strong>, not before</a></li><li><a href='#orge4c0e12'>Takeaway: The re-visit is when you see the idea.</a></li><li><a href='#org6c555df'>Thank you!</a></div></li></ul><p><a id="orgc3fae8c"></a></p><h1 id="disclaimer%3A_this_is_my_experience">Disclaimer: This is my experience</h1><p>These are my opinions. They are built up over time. This <strong>does not</strong> make them right. <hr></p><p><a id="org1b06618"></a></p><h1 id="disclaimer%3A_on_taking_notes_%28and_on_learning_in_general%29">Disclaimer: On taking notes (and on learning in general)</h1><h2 id="if_the_rules_seem_arbitrary%2C_it%27s_because_they_are">If the rules seem arbitrary, it's because they are</h2><p>Terms like yak-shaving, bike-shedding exist for a reason.</p><h2 id="o_ye_of_little_faith%2C_you_need_to_enjoy_the_process">O ye of little faith, you need to enjoy the process</h2><p>I hope my talk helps with that <hr></p><p><a id="org0da9444"></a></p><h1 id="q1_svs%3A_what_are_the_long-term_benefits_of_note-taking%2C_if_any%3F">Q1 svs: What are the Long-term Benefits of note-taking, if any?</h1><blockquote><p> What are the long term benefits of note-taking. For the moment it is a lot of effort and I can't see the payoff. Can you describe this for me? </p></blockquote><p>~ svs <hr></p><p><a id="orgdfc2b8c"></a></p><h1 id="q2_mayur%3A_what%27s_the_line_between_taking_notes_and_taking_action%3F">Q2 mayur: What's the line between taking notes and taking action?</h1><blockquote><p> Have seen myself and others putting a lot of time in structuring information (personal and org related), and sometimes it feels like it's better to just stop that and act. Where is the fine line between structuring information vs OCDing over notes? </p></blockquote><p>~ mayur <hr></p><p><a id="orgdcf6c10"></a></p><h1 id="q3_joshua%3A_how_do_you_actually_take_good_notes_that_make_sense%3F">Q3 joshua: How do you actually take good notes that make sense?</h1><blockquote><p> How do you actually takes good notes that actually makes sense , Most of the times either my notes are too short like one two words which doesn't make sense after sometime or its mostly copy paste from the source which I feel doesn't provide any value (I would rather directly read from the source) </p></blockquote><p>~ joshua <hr></p><p><a id="orgb89ca4f"></a></p><h1 id="q4_joshua%3A_how_do_i_reduce_the_number_of_notes_i%27m_taking%3F_i%27m_not_aware_of_what%27s_in_my_second_brain.">Q4 joshua: How do I reduce the number of notes I'm taking? I'm not aware of what's in my second brain.</h1><blockquote><p> How to better linking the docs and reduce the repeating of content , Let's say I am learn basics of concepts of git in one of the udemy course after sometimes I am taking another course which also going to covers git, so instead of repeating the again how one can reuse the existing notes. The main problem for me is not linking, it's I am not aware of the existing topics in my notes so I tend to repeat a content lot. </p></blockquote><p>~ joshua <hr></p><p><a id="orgaa7b179"></a></p><h1 id="fleeting_notes%3A_notes_that_are_not_%22in_my_system%22">Fleeting Notes: Notes that are not "in my system"</h1><p>By definition: this is all the writing I don't care to find again.</p><p>Some examples (I'm holding them up)</p><ul><li>grocery lists</li><li>notes in the margins of books</li><li>meeting notes too! (more on why below)</li></ul><p>Any other examples?</p><ul><li>??</li></ul><p><hr></p><p><a id="org043b12f"></a></p><h1 id="any_note_i_write_without_the_luxury_of_pausing_has_no_long-term_benefit.">Any note I write without the luxury of pausing has no long-term benefit.</h1><p>Hence, meeting notes are fleeting notes</p><p><strong>[relates to: Q1-svs]</strong></p><p>But it does have a short-term benefit &#x2026; <hr></p><p><a id="org35ba846"></a></p><h1 id="%22we_think_much_faster_than_we_speak%22">"We think much faster than we speak"</h1><p>Writing forces me to slow down and into active listening. <hr></p><p><a id="org31cf907"></a></p><h1 id="reference_notes%3A_notes_from_books_and_videos">Reference Notes: Notes from books and videos</h1><p>(also called literature or citation notes)</p><p>Notes that I take when read a book or watch a video.</p><p>Why do you think they are called Reference Notes? <hr></p><p><a id="orgf78bfc0"></a></p><h1 id="because_they_are_an_index_to_a_reference">Because they are an index to a reference</h1><p>Niclaus Luhman didn't like writing in books.</p><p>An example of a reference note:</p><ul><li><a href="denote:20230626T151147.html" class="internal-link" target="_blank">TigerStyle, or how to design safer systems in less time by Joran Dirk Greef</a></li></ul><p><hr></p><p><a id="org2afa581"></a></p><h1 id="when_taking_reference_notes%2C_don%27t_summarise%21">When taking reference notes, don't summarise!</h1><p>"either my notes are too short like one two words which doesn't make sense after sometime or its mostly copy paste from the source"</p><p><strong>[relates to: Q3-joshua]</strong> <hr></p><p><a id="org7c585eb"></a></p><h1 id="you_don%27t_need_to_take_notes_of_everything">You don't need to take notes of everything</h1><p>Effective learning requires focus</p><p>There is a difference between taking notes and bookmarking</p><p><strong>[relates to: Q2-mayur]</strong> <hr></p><p><a id="org290709d"></a></p><h1 id="writing_notes_and_reading_notes_are_separate_activities">Writing notes and Reading notes are separate activities</h1><p>Writing notes:</p><ul><li>Friction-less, instant creation</li></ul><p>Reading notes:</p><ul><li>Best-in-class searching, discoverability</li></ul><p><hr></p><p><a id="org2dedfa9"></a></p><h1 id="titles_and_tags_are_meant_for_searching">Titles and tags are meant for searching</h1><p><strong>Avoid Repetition</strong></p><p>Your "title+tags" combo should be the shortest unique string you can come up with. <hr></p><p><a id="orgea661c8"></a></p><h1 id="links_are_meant_to_help_you_discover_interesting_trails">Links are meant to help you discover interesting trails</h1><p>Forward-links, Back-links, Up-links, Down-links</p><h2 id="i_like_my_links_separate_from_my_text">I like my links separate from my text</h2><p>I don't like linking text <a href='http://example.com'>while writing it</a>, because I find it really distracting when reading it.</p><h2 id="linking_at_the_bottom_lets_me_provide_context">Linking at the bottom lets me provide context</h2><p>A good test of whether I add a link is whether I can write the context for it.</p><p>An example of such a note:</p><ul><li><a href="denote:20240514T112950.html" class="internal-link" target="_blank">Create a list of questions before you open the book</a></li></ul><p><strong>[relates to: Q2-mayur]</strong> <hr></p><p><a id="org43f7fcd"></a></p><h1 id="those_were_also_examples_of_main_notes">Those were also examples of main notes</h1><p>A main note is an atomic idea.</p><p>The atomicity helps with linking. It does not help with learning better. <hr></p><p><a id="orgc43bc43"></a></p><h1 id="%22there_are_those_who_talk_about_productivity%2C_and_then_there_are_those_who_are_productive%22">"There are those who talk about productivity, and then there are those who are productive"</h1><p>When you are creating something, focus on creating it. Don't focus on creating notes for it.</p><p><strong>[relates to: Q2-mayur]</strong> <hr></p><p><a id="orgca677c0"></a></p><h1 id="takeaway%3A_use_full_sentences_as_titles_for_your_notes.">Takeaway: Use full sentences as titles for your notes.</h1><p>Full sentences express ideas clearly. Where possible, we want one note to be about one idea. <hr></p><p><a id="org9962e02"></a></p><h1 id="takeaway%3A_avoid_repetition_in_title%2Btag.">Takeaway: Avoid repetition in title+tag.</h1><p>Remember that:</p><ol><li>You want to pack the maximum amount of "searchability" in your title + tags combination.</li><li>This is not content that you are publishing online (where tags play a different role)</li></ol><p><strong>[relates to: Q3-joshua]</strong> <hr></p><p><a id="orgeebc7b7"></a></p><h1 id="takeaway%3A_write_notes_with_an_outcome_in_mind%21">Takeaway: Write notes with an outcome in mind!</h1><p>Example Cues:</p><ul><li>Only create notes related to projects that you are <strong>actively</strong> working on! Outcome: deeper understanding</li><li>Create a note as a response when someone asks you a question (like on a forum, slack, discord). Outcome: a thoughtful response</li></ul><p>Example Notes:</p><ul><li><a href="denote:20230829T204200.html" class="internal-link" target="_blank">12a My answer to "How do I get started with Emacs with minimal config"</a></li><li><a href="denote:20230818T213157.html" class="internal-link" target="_blank">Sights, sounds, tastes, vibes of New York City, my recommendations to Kapil and Payal</a></li></ul><p><strong>[relates to: Q2-mayur]</strong> <hr></p><p><a id="orge053760"></a></p><h1 id="takeaway%3A_make_notes_**afterwards**%2C_not_before">Takeaway: Make notes <strong>afterwards</strong>, not before</h1><p>When you find yourself revisiting a topic, rewriting similar responses, or extending ideas already in your notes &#x2013; start creating new notes.</p><p>Example Notes:</p><ul><li><a href="denote:20220531T121400.html" class="internal-link" target="_blank">Joe Armstrong on Writing Code</a> and <a href="denote:20220620T163100.html" class="internal-link" target="_blank">1b2 Using Joe Armstrong's method for re-writing from scratch with git-worktrees</a></li></ul><p><strong>[relates to: Q2-mayur]</strong> <hr></p><p><a id="orge4c0e12"></a></p><h1 id="takeaway%3A_the_re-visit_is_when_you_see_the_idea.">Takeaway: The re-visit is when you see the idea.</h1><p>There is <strong>zero</strong> long-term benefit of note-taking, if you are not re-reading those notes.</p><p><strong>[relates to: Q1-svs]</strong> <hr></p><p><a id="org6c555df"></a></p><h1 id="thank_you%21">Thank you!</h1><p>Questions?</p>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/learn-speed-reading-to-accelerate-your-learning/</id>
    <link href="https://vedang.me/learn-speed-reading-to-accelerate-your-learning/"/>
    <title>Learn Speed Reading to accelerate your learning</title>
    <updated>2023-07-29T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><div class="orgtoc"></p><ul><li><a href='#orgdaad8a6'>Why should you get better at Reading?</a></li><li><a href='#org0184f7b'>The basics of Speed Reading</a></li><li><a href='#orge10a03c'>Now we are ready for a deep dive</a></div></li></ul><p><a id="orgdaad8a6"></a></p><h1 id="why_should_you_get_better_at_reading%3F">Why should you get better at Reading?</h1><p>Do you remember being taught to read? If you are like me, then you probably don't. We are taught in school when we are young and then it's just assumed that we "know how to read". But, like any other subject, you can continue to improve at reading.</p><p><strong>The better you can read, the faster you can study and the more you can retain</strong>. Reading is crucial to improving your performance on any exam.</p><p>When you are studying, you can break up your reading into two "types":</p><ol><li>Skimming the content to get a superficial view of what we are learning</li><li>Reading to thoroughly understand the content</li></ol><p><strong>We often make the mistake of directly jumping to thorough reading</strong>, but this is a slow and counter-productive approach. Instead, we should always skim first and read thoroughly afterwards.</p><p><a id="org0184f7b"></a></p><h1 id="the_basics_of_speed_reading">The basics of Speed Reading</h1><h2 id="step_1%3A_decide_beforehand_how_many_pages_you_are_going_to_read">Step 1: Decide beforehand how many pages you are going to read</h2><p>Remember the principle: The only way to get better at reading is to read more! <strong>So pick a target that is slightly outside your comfort zone and sit down to read</strong>. Here are some example targets:</p><ul><li>"I will read the next 30 pages"</li><li>"I will read Chapter 5 entirely"</li><li>"I will read subsections 5.1 and 5.2 of Chapter 5"</li></ul><h2 id="step_2%3A_map_out_the_book_so_that_you_have_it%27s_shape_and_contours">Step 2: Map out the book so that you have it's shape and contours</h2><p>Once you have decided exactly what you are going to read, get a feel for the actual work before you start.</p><p><strong>Hold the pages and flip through them like you would riffle playing cards. Let your eyes pick out interesting images and tables from what you are about to read. Does the book smell great? Do the pages feel good?</strong></p><p>What is the purpose of all this you ask? What we are doing here is called <strong>"priming"</strong>. We are teaching the brain to recognise what is going to come in our study session. This makes us receptive to the material and helps retain more of it.</p><h2 id="step_3%3A_read_the_table_of_contents_carefully">Step 3: Read the table of contents carefully</h2><p><strong>Make a mental note of all the chapter headings and subheadings in the pages you are about to read</strong>. Think of it as having appetisers (more priming). It'll make your brain want to consume the content more eagerly, because it helps you understand the structure of what you are studying, what is coming next, when you are close to finishing.</p><h2 id="step_4%3A_drag_your_finger_along_the_line_as_you_read">Step 4: Drag your finger along the line as you read</h2><p>Put your finger down on the paper and move it along the line at a fixed pace, <strong>slightly faster than forming the words in your brain</strong>. Just glide over the text and let your eyes pick up clumps of words. <strong>It will feel strange that you aren't forming full sentences in your mind, but don't worry</strong>. Your visual cortex will pick up much more than you think it will.</p><p><strong>The goal here is to not stop reading until you reach the end</strong>. It's okay to slow down if the content grabs you and pick up speed in other places, but the goal is to skim the content, not "read" it. <strong>Do not get stuck at difficult places, we will come back to those difficult places in our next reading</strong>.</p><p>Putting the finger on the page and constantly tracking it's movement with your eyes also helps you "stay in the flow" (not get distracted)</p><p><a id="orge10a03c"></a></p><h1 id="now_we_are_ready_for_a_deep_dive">Now we are ready for a deep dive</h1><p>Congratulations! You have completed the first reading of your study material. <strong>Wasn't it much faster than reading thoroughly?</strong> Now you are ready to try the techniques from <a href='https://salher.substack.com/p/the-art-of-textbook-note-taking'>The Art of Textbook Note-taking</a>! When you read the same pages again, <strong>you will surprised by how much you already remember because of your skimming</strong>.</p><p>Best of luck! Let me know how these techniques helped you, and any other techniques you already use for Speed Reading!</p>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/book-review-hows-the-road-ahead/</id>
    <link href="https://vedang.me/book-review-hows-the-road-ahead/"/>
    <title>Book Review: How's the Road Ahead? by Parul and Karan</title>
    <updated>2023-07-18T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>This book is your sneak peak into the Northeast India and it's wonderful!</p><p>Karan and Parul share their journey across the Northeast in vivid detail, and it's engrossing and tremendously entertaining.</p><p>If someone tells you they are going to bike through the North-East, you may think that it's "a big deal". But will you stop to reflect on exactly how difficult such a trip can be? Reading this book, there were so many stories where my mind went "That's completely crazy!" or "They're lucky to be alive!" or "I want to do that!". I don't think I have it in me to undertake such a journey, but I came away wanting to. And <strong>that</strong>, to me, is success as an author.</p><p>Karan's writing flows beautifully through describing the day-to-day and the reflections, the details of the North-East and the details of their personal lives. But there is a special place in my heart for the boxes that Parul has written. They are surprising, poignant, and thoughtful. I wish there were more of them.</p><p>I've been reading a lot of fantasy books this year. These are your standard brain-rot, masala-fare, escapist books &#x2013; no different from binging Netflix. This book, which I happened to pick up because of a recommendation, reminded me of why I read &#x2013; the joy of picking a book you don't know about, getting lost in the author's world, living their experience, and coming out yearning for a change.</p><p>I look forward to buying the hardcover version when it comes out. And now I know the question I must ask at any dhaba when I'm traveling :) &#x2013; "How's the Road Ahead?"</p>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/tigerstyle-or-how-to-design-safer-systems/</id>
    <link href="https://vedang.me/tigerstyle-or-how-to-design-safer-systems/"/>
    <title>TigerStyle, or how to design safer systems in less time by Joran Dirk Greef</title>
    <updated>2023-06-26T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<h1 id="executive_summary">Executive Summary</h1><ul><li>The three core ideas of Tiger Style:<ul><li>See the whole picture. (always think about design, coding, testing, operator experience)</li><li>Check yourself before you wreck yourself. (assertions)</li><li>Give yourself a time machine. (simulation)</li></ul></li><li>Developing is system is more than just coding.<ul><li>Design</li><li>Coding</li><li>Testing</li><li>Operator experience in Production</li></ul></li><li>Don't be hasty. An hour in design is worth weeks in production.</li><li>Testing is what dominates the development of complex systems.</li><li>How you paint your software from scratch is all about 4 colors and 2 textures:<ul><li>Network, Disk, Memory and CPU</li><li>Latency and Bandwidth</li></ul></li><li>Use assertions everywhere!<ul><li>The slightest logic bugs should shut everything down.</li></ul></li><li>Limit your loops with minumum and maximum bound assertions<ul><li>Everything has a limit and you must make the limits explicit.</li></ul></li><li>Design for simulation from day 1<ul><li>Simulation lets you test in days what you'd take years to find in the real world.</li></ul></li></ul><h1 id="the_video">The video</h1><p><div class="video-container"> <iframe src="https://www.youtube.com/embed/w3WYdYyjek4" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen> </iframe> </div></p><h1 id="notes_on_the_video">Notes on the video</h1><ul><li><em>(Pg 4:59)</em> "Tigerstyle sees the whole picture, checks yourself before you wreck yourself, gives you a time-machine!"</li><li><em>(Pg 6:25)</em> "Decide your tools on quality, not quantity" <strong>[tools]</strong></li><li><em>(Pg 7:25)</em> "Developing a system is not just about coding &#x2026; the design phase before and after, the coding, the testing phase after coding <span class="underline">and</span> the operator experience in production" <strong>[coding]</strong></li><li><em>(Pg 7:55)</em> "Total time of ownership = Design + Coding + Testing + Incidents" <strong>[shipping]</strong></li><li><em>(Pg 10:10)</em> "An hour in design is worth weeks in production" <strong>[quotes]</strong><ul><li>by Joran Dirk Greef</li></ul></li><li><em>(Pg 10:40)</em> "Simple and Elegant Systems tend to be easier and faster to design and get right, more efficient in execution and much more reliable" <strong>[quotes]</strong><ul><li>by Edsger Dijkstra</li></ul></li><li><em>(Pg 13:10)</em> "Testing is what dominates the development of complex systems." <strong>[testing]</strong></li><li><em>(Pg 13:15)</em> "Callout to Jepsen as a great tool for testing distributed systems!" <strong>[jepsen, clojure]</strong></li><li><em>(Pg 15:25)</em> "There is more to software than just engineering. It is at least the intersection of engineering and art." <strong>[building]</strong></li><li><em>(Pg 18:40)</em> "Four colors of systems programming: Network, Disk, Memory, CPU" <strong>[distributedsystems]</strong></li><li><em>(Pg 18:56)</em> "The two textures that all these colors share are: latency and bandwidth" <strong>[distributedsystems]</strong></li><li><em>(Pg 19:10)</em> "In the design phase &#x2026; 1) Back of the envelope calculations" <strong>[design]</strong></li><li><em>(Pg 19:30)</em> "Sketch as many solutions as you can using &#x2026; (colors, textures) .. because sketches are cheap and rewrites are expensive" <strong>[design]</strong></li><li><em>(Pg 19:40)</em> "Find great names for your components &#x2026; TigerStyle doc in our repo gives you tips on naming" <strong>[design]</strong></li><li><em>(Pg 20:35)</em> "Define your mechanical fault models" <strong>[design]</strong><ul><li>Think through your fault models and see if your system is designed accordingly.</li></ul></li><li><em>(Pg 21:10)</em> "Be explicit up front about the fault models your system can tolerate" <strong>[design]</strong></li><li><em>(Pg 21:40)</em> "Stateless is always better than stateful" <strong>[design,state]</strong><ul><li>State is very hard! :D</li></ul></li><li><em>(Pg 22:00)</em> "It pays to reduce surface area that you commit to. The more surface area, the more unexpected interactions." <strong>[design]</strong></li><li><em>(Pg 23:00)</em> "Zero Technical Debt policy &#x2026; do the best that you currently can &#x2026; do it right the first time" <strong>[design]</strong><ul><li>Knowing that your work is solid motivates your team</li></ul></li><li><em>(Pg 24:00)</em> "Walk and Talk when you are discussing hard problems" <strong>[design]</strong><ul><li>Walking and discussing is really simulating for the brain.</li><li>Design tricks put you in good place for coding.</li></ul></li><li><em>(Pg 26:30)</em> "Assertions: NASA's power of ten rules for safety critical code" <strong>[reference]</strong><ul><li>Your system should run safely or not at all .. shut down in the face of a bug.</li></ul></li><li><em>(Pg 27:30)</em> "Write assertions checking your function arguments" <strong>[correctness]</strong><ul><li>that they are as you expect, and also assert that they are correct in relation to each other.</li><li>and you do the same for the return value</li><li>now your functions are contributing to correctness</li></ul></li><li><em>(Pg 27:42)</em> "The ultimate purpose of abstraction is for correctness" <strong>[quotes]</strong><ul><li>By Joran</li></ul></li><li><em>(Pg 29:00)</em> "If it compiles, you've just begun!" <strong>[correctness]</strong></li><li><em>(Pg 30:20)</em> "Start to think like a hacker: what don't you expect? What would be unusual in this case?" <strong>[hackers]</strong></li><li><em>(Pg 32:40)</em> "Everything has a limit &#x2026; systems have limits .. make them explicit so that your design can handle edge-cases" <strong>[limits, distributedsystems]</strong></li><li><em>(Pg 34:09)</em> "Static memory allocation is just one example of everything having a limit." <strong>[limits]</strong><ul><li>Memory, Network, CPU, Disk all have limits and need to apply back-pressure.</li></ul></li><li><em>(Pg 36:10)</em> "Assertions are a force-multiplier for testing" <strong>[testing]</strong><ul><li>Do not let your programming language turn them off in production!</li></ul></li><li><em>(Pg 45:30)</em> "Assertions document invariants" <strong>[testing]</strong><ul><li>Now people can see the positive space (your logic) as well as the negative space (what shouldn't happen)</li><li>That is what teachers do, they want to teach you the right way and also the wrong way.</li></ul></li><li><em>(Pg 47:30)</em> "(our fuzzy simulator) .. push the faults to the very theoretical limit .. at rapid time" <strong>[fuzzying, testing, distributedsystems]</strong></li></ul>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/yaron-minsky-rules-for-effective-programming/</id>
    <link href="https://vedang.me/yaron-minsky-rules-for-effective-programming/"/>
    <title>Yaron Minsky: Rules for Effective Programming</title>
    <updated>2023-05-26T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><em>Snippet from <a href='https://www.youtube.com/watch?v=-J8YyfrSwTk'>Effective ML</a></em></p><ol><li><strong>Favour Readers Over Writers</strong>: Readers drive clarity and simplicity. Always write code such that it's easy to read</li><li><strong>Create Uniform Interfaces</strong>: Uniform interfaces help set expectations for how the code/data can be treated. They make it easier to jump into new code.</li><li><strong>Make Illegal States Unrepresentable</strong>: Think about the invariants of your data, and design datastructures so that your data cannot enter into an illegal state.</li><li><strong>Code for Exhaustiveness</strong>: Prefer explicit case matching over implicit default cases. A default case is a bug waiting to happen when the shape of the data changes.</li><li><strong>Open Few Modules</strong>: Or in the more general case, use namespaced functions to improve readability. OCaml has a nifty feature where you can use namespaced functions in a local scope! This improves the readability even more than having to require the namespace for the whole file.</li><li><strong>Make Common Errors Obvious</strong>: Make it explicit in the function name that this function can throw an exception. Then the caller knows to expect / handle a possible error.</li><li><strong>Avoid Boilerplate</strong>: Reduces code readability, increases chances of subtle bugs.</li><li><strong>Avoid Complex Type Hackery</strong>: The enemy of correctness is complexity. And complex types are complex.</li><li><strong>Do not be puritanical about purity</strong>: Remember that side-effects are the only way you actually change anything in the real world.</li></ol>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/tabfs-and-automation/</id>
    <link href="https://vedang.me/tabfs-and-automation/"/>
    <title>TabFS and Automation</title>
    <updated>2022-09-20T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>Hello reader! This is a short update about the very cool <a href='https://omar.website/tabfs/'>TabFS</a>, which is a browser extension that maps your open tabs to the filesystem using Fuse. Think <code>/proc</code> but for the browser. This is an excellent idea, because now I can run all the tools that I am already familiar with on my tabs!</p><p>As I was reading through the TabFS page (linked above), I thought to myself: "this is so much RC energy!". So I checked, and lo and behold, Omar Rizwan is an RC Alum :D</p><h1 id="why_i_wanted_something_like_tabfs">Why I wanted something like TabFS</h1><p>The internet is full of interesting stuff that hijacks my attention. I end up getting completely distracted from my planned tasks. My solution to this is: I open all the links I want, but I don't read anything immediately. Instead, I capture the link in Org-Mode and schedule it for later (generally tomorrow). This way, when I am planning the day, I have a bunch of links in my Agenda. I choose which ones I want to read on any given day and defer / delete the rest. I use the excellent <a href='https://orgmode.org/manual/Agenda-Commands.html'>Bulk Scatter</a> tool built into Org Agenda for this.</p><p>I thought: Why do this manually? Enter TabFS</p><h1 id="a_small_babashka_script">A small Babashka script</h1><p>With TabFS installed, it was a small step to go from there to <code>org-mode</code>. My go-to scripting tool is <a href='https://book.babashka.org/'>Babashka</a>. Here is the meat of the code:</p><pre><code class="language-clojure">&#40;require '&#91;babashka.fs :as fs&#93;
         '&#91;clojure.string :as cs&#93;&#41;

&#40;defn build-tab-info
  &quot;Collects the title and URL for all open tabs using TabFS.&quot;
  &#91;opts&#93;
  &#40;map &#40;fn &#91;t&#93;
         {:title &#40;first &#40;fs/read-all-lines &#40;str t &quot;/title.txt&quot;&#41;&#41;&#41;
          :url &#40;first &#40;fs/read-all-lines &#40;str t &quot;/url.txt&quot;&#41;&#41;&#41;}&#41;
       &#40;fs/list-dir &#40;:tab-dir opts&#41;&#41;&#41;&#41;

&#40;def plain-text-template
  &quot;The template for creating the org-mode plain-text entry. Placeholders
are for the following items &#40;in order&#41;:
  1. Org-Mode Heading level: default value is 1.
  2. URL of the link to the stored.
  3. Title of the link to be stored.
  4. Date when the Tab is scheduled for reading: default is tomorrow.
  5. Date when the Tab was captured.&quot;
  &quot;%s TODO &#91;&#91;%s&#93;&#91;%s&#93;&#93;\nSCHEDULED: %s\n%s&quot;&#41;

&#40;defn tabs-&gt;text
  &quot;Convert the list of open tabs to a wall of text that can be stored in
  Plain-Text.&quot;
  &#91;opts&#93;
  &#40;-&gt;&gt; &#40;build-tab-info opts&#41;
       &#40;map &#40;fn &#91;t&#93;
              &#40;format plain-text-template
                      &#40;apply str &#40;repeat &#40;:org-mode-heading-level opts&#41; &quot;&#42;&quot;&#41;&#41;
                      &#40;:url t&#41;
                      &#40;:title t&#41;
                      &#40;:scheduled-on opts&#41;
                      &#40;:captured-on opts&#41;&#41;&#41;&#41;
       &#40;cs/join \newline&#41;&#41;&#41;
</code></pre><p>I wrote a <a href='https://github.com/vedang/bb-scripts#bb-plain-text-tabs'>CLI script</a> for the code, here is what the <code>help</code> command looks like:</p><pre><code>✦ ➜ bb-plain-text-tabs --help
Convert all open tabs to plain text Org Mode format. Optionally, close the open tabs.

Usage: bb-plain-text-tabs &#91;options&#93;

Options:
  -fs,  --tabfs-mnt-path                &#42;REQUIRED&#42; TabFS mount path. The path on the filesystem where TabFS is mounted.
  -hl,  --org-mode-heading-level        1                            The heading level at which link headings will be stored in Org Mode.
  -s,   --scheduled-on           &lt;date&gt; tomorrow                     Date on which to schedule reading this batch of links, in yyyy-MM-dd format.
  -del, --delete-tabs            &lt;del&gt;  false                        Boolean value to indicate if browser tabs should be closed

Examples:
   bb-plain-text-tabs -fs &lt;TabFS Mount Path&gt; # Convert Open Tabs to Plain Text
   bb-plain-text-tabs -fs &lt;TabFS Mount Path&gt; --delete-tabs # Delete tabs and print plain text
   bb-plain-text-tabs -fs &lt;TabFS Mount Path&gt; | tee tabs.org # Capture Plain Text output to file
</code></pre><h1 id="what_it_looks_like">What it looks like</h1><p>Finally, this is the output, in Org Mode:</p><p><img src="/assets/images/tabfs.png" alt="img" title="Open Tabs moved to org-mode, and automatically closed!" /></p><p>I hope all the Emacs/Org-mode folks reading this find it useful! Thank you to Omar for the excellent <a href='https://omar.website/tabfs/'>TabFS</a> tool!</p>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/gitlab-private-pages-docker/</id>
    <link href="https://vedang.me/gitlab-private-pages-docker/"/>
    <title>Gitlab Private Pages + Running Docker containers in Gitlab CI/CD Pipelines</title>
    <updated>2022-08-23T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<h1 id="private_pages_on_gitlab">Private pages on Gitlab</h1><p>Did you know that <a href='https://docs.gitlab.com/ee/user/project/pages/'>Gitlab Pages</a> supports private / auth-based static sites? Basically, you add people you want to share your site with as Project Members to the repository. Gitlab then enforces Gitlab Login on the website, so you can only see the content if you are logged into Gitlab (and have been given access to the repository).</p><p>This is a very cool way to host private static sites freely (vs, for example, self-hosting + HTTP Basic Auth).</p><h1 id="moving_from_github_actions_to_gitlab_ci%2Fcd_pipelines_%28%2B_running_docker_containers_in_ci%2Fcd_scripts%29">Moving from Github Actions to Gitlab CI/CD Pipelines (+ running Docker containers in CI/CD scripts)</h1><p>It's ridiculously hard to Google for the right way to run a dockerized tool in CI/CD environments.</p><p>Github Actions makes this a nobrainer because their images (example: <code>ubuntu-latest</code>) have <code>docker</code> clients and servers baked in already. So you just write your command (say <code>docker run hello-world</code>) directly into your YAML file and you are done.</p><p>Gitlab CI/CD does not have this. They expect you to search + use images from Dockerhub / Gitlab Container Registry. To make matters worse, it isn't enough to use the official Docker in Docker image (which is <code>docker:latest</code> btw). Using this image only gives you the <code>docker</code> client. You still need to run the <code>docker</code> server separately. If you don't, your pipeline will fail with the following error:</p><pre><code>docker: error during connect: Post &quot;http://docker:2375/v1.24/containers/create&quot;: dial tcp: lookup docker on 169.254.169.254:53: no such host.
</code></pre><p>To run the server, you need to understand the <a href='https://docs.gitlab.com/ee/ci/services/'>Services</a> concept of Gitlab CI/CD. In short, this is a way to run services that your main job might need to access when it runs. You can use it, for example, to run database instances that your test job would connect to. In our case, we need a docker daemon service, which is provided by the <code>docker:dind</code> image.</p><p>Once you have this, then your entire pipeline will finally run. It took me a frustratingly long time to figure it all out. Here is what my final <code>.gitlab-ci.yml</code> file looks like:</p><pre><code class="language-yaml">workflow:
  rules:
    - if: $CI&#95;COMMIT&#95;BRANCH

before&#95;script:
  - mkdir -p .neuron/output &amp;&amp; touch .neuron/output/.nojekyll

pages:
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker run -v $PWD/content:/notes sridca/neuron neuron gen --pretty-urls
    - cp -R content/.neuron/output public
  artifacts:
    paths:
      - public
  rules:
    - if: $CI&#95;COMMIT&#95;BRANCH == &quot;master&quot;
</code></pre><p>The <code>pages</code> job-name is special. Gitlab understands that it is meant for hosting, and expects HTML / assets in a top-level directory called <code>public</code>.</p><p>I hope this helps someone else waste less time.</p>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/nicole-forsgren-four-key-metrics-of-high-performance/</id>
    <link href="https://vedang.me/nicole-forsgren-four-key-metrics-of-high-performance/"/>
    <title>Nicole Forsgren: The Four Key Metrics of High Performance</title>
    <updated>2022-08-19T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><em>Snippet from <a href='https://itrevolution.com/book/accelerate/'>Accelerate</a></em></p><ul><li><strong>Lead Time</strong> : Length of time between when the code is committed and when it is deployed to production. Smaller is better.</li><li><strong>Mean Time to Recover</strong> : Length of time from incident start to incident end. Smaller is better.</li><li><strong>Change Failure Rate</strong> : Number of deployments to production that need hotfixes. Smaller is better.</li><li><strong>Deployment Frequency</strong> : Number of times new code (not hotfixes) is deployed to production. Larger is better.</li></ul><p>Improving these metrics directly results in huge performance boosts for developer productivity.</p>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/marc-randolph-rules-for-success/</id>
    <link href="https://vedang.me/marc-randolph-rules-for-success/"/>
    <title>Marc Randolph: Rules for Success</title>
    <updated>2022-08-12T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><em>Snippet from <a href='https://www.amazon.com/That-Will-Never-Work-Netflix/dp/0316530204'>That Will Never Work</a></em></p><ol><li>Do at least 10% more than you are asked.</li><li>Never, ever, to anybody, present as fact opinions on things you don't know.</li><li>Be courteous and considerate always - up and down.</li><li>Don't knock, don't complain - stick to constructive, serious criticism.</li><li>Don't be afraid to make decisions when you have the facts on which to make them.</li><li>Quantify where possible.</li><li>Be open-minded but sceptical.</li><li>Be prompt.</li></ol>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/tim-peters-the-python-way/</id>
    <link href="https://vedang.me/tim-peters-the-python-way/"/>
    <title>Tim Peters: The Python Way</title>
    <updated>2022-07-04T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><em>Snippet from: <a href='https://mail.python.org/pipermail/python-list/1999-June/001951.html'>The Python Mailing List</a></em></p><ol><li>Beautiful is better than ugly.</li><li>Explicit is better than implicit.</li><li>Simple is better than complex.</li><li>Complex is better than complicated.</li><li>Flat is better than nested.</li><li>Sparse is better than dense.</li><li>Readability counts.</li><li>Special cases aren't special enough to break the rules.</li><li>Although practicality beats purity.</li><li>Errors should never pass silently.</li><li>Unless explicitly silenced.</li><li>In the face of ambiguity, refuse the temptation to guess.</li><li>There should be one - and preferably only one - obvious way to do it.</li><li>Although that way may not be obvious at first unless you're Dutch.</li><li>Now is better than never.</li><li>Although never is often better than <strong>right</strong> now.</li><li>If the implementation is hard to explain, it's a bad idea.</li><li>If the implementation is easy to explain, it may be a good idea.</li><li>Namespaces are one honking great idea &#x2013; let's do more of those!</li></ol>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/using-joe-armstrongs-method-for-re-writing-from-scratch/</id>
    <link href="https://vedang.me/using-joe-armstrongs-method-for-re-writing-from-scratch/"/>
    <title>Using Joe Armstrong's method for re-writing from scratch with git-worktrees</title>
    <updated>2022-06-20T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>Joe Armstrong used a simple principle when programming: "Throw away the first implementation, you didn't actually understand the problem when you wrote it."<sup><a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink">1</a></sup>. In my own work, I've developed a way to start from scratch while keeping the multiple histories of the project linked together. These are the steps:</p><h1 id="create_the_following_folder_structure_to_get_started.">Create the following folder structure to get started.</h1><p>For example, here we are developing the <code>automerge-clj</code> project:</p><pre><code class="language-sh">mkdir -p &#91;work-dir&#93;/automerge-clj/v1/
cd !$
clojure -Tnew lib :name me.vedang/automerge-clj
cd automerge-clj
git init
</code></pre><p>The <code>clojure new</code> command creates another <code>automerge-clj</code> directory inside our <code>v1</code> directory. This is going to be the master repo that will drive our prototyping.</p><h1 id="create_an_initial_commit_of_the_bare-minimum_skeleton.">Create an Initial commit of the bare-minimum skeleton.</h1><p>This gives us a good starting point to come back to.</p><p>Now we make as many commits in <code>automerge-clj/v1/automerge-clj</code> as we want.</p><h1 id="once_we_feel_the_need_to_start_a_new_prototype">Once we feel the need to start a new prototype</h1><p>We use <code>git worktree</code> to create a new "place" to start from:</p><pre><code class="language-sh">cd &#91;work-dir&#93;/automerge-clj/v1/automerge-clj
git worktree add ../../v2
cd ../../v2 # Here, git will have automatically created a new branch for us, called v2.
</code></pre><p>We need to create an orphaned branch at the starting point of the repo in order to start from scratch again. We can also do a <code>git reset --hard</code> with the rev-list command to start from a previous commit.</p><p>To start from scratch, discarding even the initial commit:</p><pre><code class="language-sh">git checkout --orphan auto-v2
git rm -rf .
</code></pre><p>To reset the current branch to a previous commit</p><pre><code class="language-sh">git reset --hard $&#40;git rev-list HEAD | tail -n 1&#41;
</code></pre><p><strong>Caveat</strong>: Note that it's <code>v1/automerge-clj/deps.edn</code> (for example) vs just <code>v2/deps.edn</code>. This is a gotcha but I don't bother working around it. I find I get used to it almost immediately.</p><h1 id="cleaning_up_old_versions">Cleaning up old versions</h1><p>When done with prototypes, or when you want to clean up space, you can just:</p><pre><code class="language-sh"># from the &quot;main&quot; v1/project directory
git worktree list
git worktree remove path
</code></pre><h2 id="footnotes">Footnotes</h2><p><sup><a id="fn.1" class="footnum" href="#fnr.1">1</a></sup> See: <a href="denote:20220531T121400.html" class="internal-link">Joe Armstrong on Writing Code</a> for the context of the quote</p>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/joe-armstrong-on-writing-code/</id>
    <link href="https://vedang.me/joe-armstrong-on-writing-code/"/>
    <title>Joe Armstrong on Writing Code</title>
    <updated>2022-05-31T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>From: <a href='https://github.com/lukego/blog/issues/32#issue-435504246'>Luke Gorrie: Joe the Office Mate</a></p><blockquote><p> Joe wrote amazingly simple programs and he did so in a peculiar way. </p><p> First he wrote down the program any old way just to get it out of his head. Then once it worked he would then immediately create a new directory program2 and write it again. He would repeat this process five or six times (program5, program6, &#x2026;) and each time he would understand the problem a little better and sense which parts of the program were essential enough to re-type. </p><p> He thought this was the most natural thing in the world: of course you throw away the first few implementations, you didn't understand the problem when you wrote those! </p></blockquote><p>I have thought deeply about this method of working and love it! It's simple, elegant and has made me a better programmer!</p>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/lt-how-to-give-lt/</id>
    <link href="https://vedang.me/lt-how-to-give-lt/"/>
    <title>A lighting talk on how to give lighting talks</title>
    <updated>2022-01-28T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><em>These are the slides and speaker notes from a talk I gave in Jan 2022 about effective Lightning Talks and why we developed this culture at Helpshift. I had previously given this same talk in earlier years at least twice. This week (in Oct 2024), the topic came up again, so I am lightly polishing and publishing these notes to the wider audience</em></p><p><div class="orgtoc"></p><ul><li><a href='#org41fa50f'>Question to the Audience: What do you expect from Lightning talks?</a></li><li><a href='#org07a7677'>The 'Why' of Lightning Talks?</a></li><li><a href='#org5b478b2'>Question to the Audience: What are good lightning talks according to you?</a></li><li><a href='#org59c7dbf'>The 'How' of Lightning Talks</a></li><li><a href='#orgf87a872'>Some hacks we've come up with over time</a></li><li><a href='#orgb1734df'>What is LT <strong>NOT</strong></a></li><li><a href='#h:BDE64570-777C-48ED-B8BC-A8FB51C56957'>Fin!</a></div></li></ul><p><a id="org41fa50f"></a></p><h1 id="question_to_the_audience%3A_what_do_you_expect_from_lightning_talks%3F">Question to the Audience: What do you expect from Lightning talks?</h1><p>⚡🌩️☁️❓</p><p>Audience Answers:</p><ul><li>To learn something interesting.</li><li>If there is a big production issue, people should know about it.</li><li>To learn how other departments are working.</li><li>To teach how we've solved existing problems.</li><li>To share the latest new thing that you learnt, and what problem it solved for you.</li><li>To share general news about the well-being of the team, company, customers.</li><li>To show off newly released product features.</li><li>To show off deployment and release pipeline improvements.</li></ul><p>These are all valid and incredible answers. Here are some I thought of as I prepared these slides <hr></p><p><a id="org07a7677"></a></p><h1 id="the_%27why%27_of_lightning_talks%3F">The 'Why' of Lightning Talks?</h1><ul><li><strong>To make each other better</strong></li><li>To <strong>learn</strong> about each other and our interests</li><li>To <strong>bounce ideas off</strong> other smart people, and learn from unexpected sources</li><li>To <strong>know what is happening in the company</strong> and even in the world</li></ul><p>One of the best ways to become better is to actively share everything you are doing.</p><p>It is also a strong way to build great culture. <hr></p><p><a id="org5b478b2"></a></p><h1 id="question_to_the_audience%3A_what_are_good_lightning_talks_according_to_you%3F">Question to the Audience: What are good lightning talks according to you?</h1><ul><li>An <strong>interesting bug</strong>! Unexpected behavior or Security Exploit</li><li>A <strong>debugging session</strong>! How did you follow the trail from symptom to cause?</li><li><strong>Productivity tips</strong>!</li><li><strong>Hobbies</strong>!</li><li><strong>Explainers</strong>!</li><li>and so much more.</li></ul><p>An idea that we were discussing yesterday: What if we build a search server to consume content from Confluence and make it easily searchable? I have nothing but this idea. Can I write a small bit of code or scripting and show what this might look like on some existing content? That would be a great lightning talk! <hr></p><p><a id="org59c7dbf"></a></p><h1 id="the_%27how%27_of_lightning_talks">The 'How' of Lightning Talks</h1><ul><li>When in doubt, <strong>keep it short</strong>. Ideally, 5 to 10 minutes.</li><li>The longer the presentation, the more <strong>polished</strong> it needs to be to keep everyone hooked.</li><li>Engaging the audience is a good idea. It's a core <strong>why</strong> principle.</li></ul><p>Feel free to ramble if you are speaking for 5 minutes. People don't mind 5 minutes of not understanding something. 25 minutes of not understanding something on the other hand &#x2026; <hr></p><p><a id="orgf87a872"></a></p><h1 id="some_hacks_we%27ve_come_up_with_over_time">Some hacks we've come up with over time</h1><ul><li>A <strong>strict moderator</strong>, who keeps a running clock.</li><li>Compulsorily talk about <strong>Incident Reports</strong>. (better follow-through needed here)</li><li>Talk about <strong>interesting bugs</strong>! (bring Support and Product anecdotes)</li><li><strong>Demo</strong> your features/changes. (tell us what you've been up to!)</li><li>Talk to the audience, make it a conversation. Add MEMES!</li></ul><p>Notes from the audience:</p><ul><li>Build a story!</li><li>It's okay to talk about stuff outside of work also.</li></ul><p><hr></p><p><a id="orgb1734df"></a></p><h1 id="what_is_lt_**not**">What is LT <strong>NOT</strong></h1><ul><li>Does not need to be a fancy presentation, we need to reset the precedent here.</li><li>Does not need to be entertaining (though that's good to have)</li></ul><p>The 5-min rule guards against something being boring to one segment of the audience. It also lets more people take the stage.</p><p>We all agree that we learn more in 2 weeks of Oncall than we do in 6 months of normal work. This is actually a sign that we are wasting our potential. Talking about incidents helps everyone learn! <hr></p><p><a id="h:BDE64570-777C-48ED-B8BC-A8FB51C56957"></a></p><h1 id="fin%21">Fin!</h1><p><em>⚡ Thank you! Let's learn together! ⚡</em></p><p>We all agree that we learn more in 2 weeks of Oncall than we do in 6 months of normal work. This is actually a sign that we are wasting our potential, we can learn much more rapidly than we give ourselves credit for. Sharing and teaching what you know makes everyone better! Let's do more of it and get smarter all around!</p>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/lilys-recipes/</id>
    <link href="https://vedang.me/lilys-recipes/"/>
    <title>Lily's Recipes</title>
    <updated>2022-01-11T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><div class="org-center"> <p> <i>This post was originally written in 2017. I've updated it today (11th Jan 2022), after my Ajji passed away. May she enjoy endless runs of cooking shows in her afterlife.</i> </p> </div></p><p>Lily is a lifelong cooking enthusiast. She collected, cooked, commented on, and critiqued thousands of recipes over the years - and she did it before the age of the telephone (let alone the TV, the computer or the internet).</p><p><div class="portrait"> <img src="/assets/images/ajji.jpg" alt="img" title="Leela Manerikar: 29th Oct 1920 &#x2013; 9th Jan 2022" /> </div></p><p>The photo you see here is of my ajji (grandmother) Leela Manerikar - lovingly referred to as Lily by her numerous grandchildren. She's 96 in the photo.</p><p>I moved out of my parents' home in 2015, and learnt two things quickly:</p><ol><li>I was an ungrateful son who never truly appreciated the work that goes into setting a dinner table everyday.</li><li>I liked cooking. I liked the ritual of it, I loved the flavours, and it helped me relax.</li></ol><p>My grandmother - and my parents - were pleasantly surprised to hear about my newfound love of cooking. And then Lily said, "Are you interested in checking out my collection of recipes?". My grandmother watched every cooking show she could ("Vegetarian only, mind you.") but I did not know that she collected recipes. I was more than happy to go through her recipes.</p><p>These last years have been harsh on Lily. She lost her eyesight, and has trouble moving around the house. She cannot cook anymore, but she is delighted that I enjoy it, and suggests improvements to my recipes when I describe my successes and missteps to her.</p><p>Sadly, we've lost all but one box of Lily's recipes over the years. Some to poor storage and some to bookworms. As she handed over this final box of recipes to me, she said:</p><blockquote><p> "I used to collect these recipes obsessively when I was young. You kids have it easy: look at your phone and it will tell you how to cook something, how it should look, what it should smell like. In my time, gathering this knowledge was harder. These scraps may or may not be useful to you, what with your fancy phone. If you find something useful, great. Otherwise, throw them away." </p></blockquote><p>That last bit about "throwing them away" comes from a sense of drama cultivated by watching a healthy dose of daily soaps and cooking shows for decades. None-the-less, these clippings are precious to me. They are a glimpse into the life of a person I've never known - a young woman who raised four kids on a teacher's salary. Someone who was a long way off from being my grandmother. There are scribbles on the sides, marking improvements and experiments over time. There are postcards of recipes exchanged between my grandmom and her daughter - studying cooking in a Banglorean college. There is the awe of looking at something that is older than I am, and represents a bygone era.</p><p>I want Lily's legacy to live on in a small way, and to be accessible to all her grandchildren and great-grandchildren. And so I'm moving these recipes online, out of reach of bookworms and moisture (I'll be on the lookout for bitworms). I plan to cook the dishes, expand on the annotations in the corners and raise a toast to my grandmother's health as often as I can!</p>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/clj-rebl-lein-cider-emacs/</id>
    <link href="https://vedang.me/clj-rebl-lein-cider-emacs/"/>
    <title>How to use the Clojure REBL with Leiningen, Cider and Emacs</title>
    <updated>2020-10-28T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>Note&#42;: This set of instructions only work on <strong>Clojure 1.10+</strong>, <strong>Lein 2.9+</strong> and <strong>JDK 11+</strong>. Also, REBL requires a license for commercial use.</p><p>I didn't find a handy reference to getting started with REBL if your primary workflow is Leiningen/nrepl, so here is the series of things I had to do to get the REBL up and running for myself.</p><ol><li>Download the <a href='https://docs.datomic.com/cloud/other-tools/REBL.html'>REBL jar</a> from Cognitect.</li><li>Use <a href='https://github.com/kumarshantanu/lein-localrepo'>lein-localrepo</a> to install the REBL jar to your m2.<pre><code class="language-shell-script">    lein localrepo install rebl-0.9.242/rebl-0.9.242.jar com.cognitect/rebl 0.9.242
    </code></pre></li><li>Add the following code to your <code>profiles.clj</code> file (code below). This is based on the <a href='https://docs.datomic.com/cloud/other-tools/REBL.html#installation'>REBL installation guide</a> and <a href='https://github.com/RickMoynihan/nrebl.middleware'>Rick Moynihan's nrepl middleware</a>:<pre><code class="language-clojure">    {
     ;; ....
     ;; You other profiles stuff
     ;; ....
     :rebl {:plugins &#91;&#91;cider/cider-nrepl &quot;0.25.3&quot;&#93;
                      &#91;refactor-nrepl &quot;2.5.0&quot;&#93;
                      &#91;nrepl &quot;0.8.2&quot;&#93;&#93;
            :repl-options {:nrepl-middleware &#91;nrebl.middleware/wrap-nrebl&#93;}
            :dependencies &#91;&#91;org.clojure/tools.deps.alpha &quot;0.7.511&quot;&#93;
                           &#91;com.cognitect/rebl &quot;0.9.242&quot;&#93;
                           &#91;org.clojure/core.async &quot;1.3.610&quot;&#93;
                           &#91;cljfmt  &quot;0.6.4&quot;&#93;
                           &#91;rickmoynihan/nrebl.middleware &quot;0.3.1&quot;&#93;
                           &#91;org.openjfx/javafx-fxml &quot;15-ea+6&quot;&#93;
                           &#91;org.openjfx/javafx-controls &quot;15-ea+6&quot;&#93;
                           &#91;org.openjfx/javafx-swing &quot;15-ea+6&quot;&#93;
                           &#91;org.openjfx/javafx-base &quot;15-ea+6&quot;&#93;
                           &#91;org.openjfx/javafx-web &quot;15-ea+6&quot;&#93;&#93;}
     ;; ....
     ;; If you don't have anything in your profiles.clj right now, just
     ;; remove the comments and copy the whole map over.
     }
    </code></pre></li><li>Start the REPL with the new profile<pre><code class="language-shell-script">    lein with-profile +rebl repl :headless
    </code></pre></li><li>Connect to the REPL from Emacs. Do this by opening up any Clojure file in your project and executing <code>M-x cider-connect</code> in the file.</li><li>Run the following commands in the REPL to spin up the REBL.<pre><code class="language-clojure">    &#40;require '&#91;cognitect.rebl :as rebl&#93;&#41;
    &#40;rebl/ui&#41;
    </code></pre></li><li>At this point, anything that you evaluate when programming will be sent to your REPL as well as to your REBL and can be inspected from there. Anything you evaluate into the REPL will be sent over to the REBL as well.</li><li>Work on the REPL as you normally would. Your work is also being sent into the REBL for fancy viewing when you need it.</li></ol>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/programmer-guide/</id>
    <link href="https://vedang.me/programmer-guide/"/>
    <title>The Programmer's Guide to Wisdom</title>
    <updated>2020-08-22T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><img src="/assets/images/programmer_guide.png" alt="img" title="Timeless Advice on Programming, from the pros" /></p><blockquote><p> Over the weekend, I created a poster for my desk. It captures timeless best practices and advice on programming and software design. Presenting it here, in the hopes that it'll be useful to others as well. Features: <a href='https://twitter.com/esrtweet'>@esrtweet</a>, Doug McIlroy, <a href='https://twitter.com/rob_pike'>@rob<sub>pike</sub></a> and <a href='https://twitter.com/RealGeneKim'>@RealGeneKim</a> </p><p><ul><li><a href='https://twitter.com/vedang/status/1292744222873853952'>@vedang</a>, <a href='https://twitter.com/vedang/status/1292744222873853952'>14:16 Aug, 10, 2020</a></li></ul></p></blockquote><p>I collect such deeply useful and universal advice <a href='/tags/wisdom'>under the tag wisdom</a> if you want to read more posts of this kind. To create this poster, I've referred to the following notes:</p><ul><li><a href="denote:20180612T095600.html" class="internal-link" target="_blank">ESR: the UNIX Philosophy</a></li><li><a href="denote:20180612T100100.html" class="internal-link" target="_blank">Doug McIlroy: the UNIX philosophy</a></li><li><a href="denote:20180612T100500.html" class="internal-link" target="_blank">Rob Pike: Rules of Programming</a></li><li><a href="denote:20200809T100700.html" class="internal-link" target="_blank">Gene Kim: The Five Ideals of DevOps</a></li><li><a href="denote:20200811T100900.html" class="internal-link" target="_blank">Gene Kim: The Three Ways of doing excellent Work</a></li><li><a href="denote:20200811T101100.html" class="internal-link" target="_blank">Gene Kim: The Four Types of Work</a></li></ul>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/gene-kim-the-four-types-of-work/</id>
    <link href="https://vedang.me/gene-kim-the-four-types-of-work/"/>
    <title>Gene Kim: The Four Types of Work</title>
    <updated>2020-08-11T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><em>Snippet from: <a href='https://www.amazon.in/Phoenix-Project-DevOps-Helping-Business-ebook/dp/B078Y98RG8/'>The Phoenix Project</a>, along with my own notes</em></p><ul><li><strong>Business Projects</strong> : "Feature Work". This is the most visible type of work.</li><li><strong>Internal IT Projects</strong> : Release Automation, QA Automation, Developer Tooling and other internal enablers. Mostly un-tracked and invisible, but crucial to long-term success.</li><li><strong>Updates and Changes</strong> : Generally generated from above two categories of work. Introduces delay as breadth of existing surface area increases.</li><li><strong>Unplanned Work</strong> : Fire-fighting at all levels of the company. Ruins planned work, so root causes need to be aggressively remediated.</li></ul>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/gene-kim-the-three-ways/</id>
    <link href="https://vedang.me/gene-kim-the-three-ways/"/>
    <title>Gene Kim: The Three Ways of doing excellent Work</title>
    <updated>2020-08-11T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><em>Snippet from: <a href='https://www.amazon.in/Phoenix-Project-DevOps-Helping-Business-ebook/dp/B078Y98RG8/'>The Phoenix Project</a>, along with my own notes</em></p><ul><li><strong>Flow</strong> : Maximizing the rate of flow of work is the key to success. Limiting the work in progress is the fastest way to achieve Flow.</li><li><strong>Fast Feedback</strong> : Setup systems to get fast feedback at every stage of work, from concept through shipping to maintaining in production.</li><li><strong>Experimentation and Learning</strong> : Keep dedicated time for experiments, at every level of the company. A culture of innovation is necessary for achieving and maintaining Flow and Feedback.</li></ul>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/l-peter-deutsch-the-fallacies-of-distributed-computing/</id>
    <link href="https://vedang.me/l-peter-deutsch-the-fallacies-of-distributed-computing/"/>
    <title>L Peter Deutsch: The Fallacies of Distributed Computing</title>
    <updated>2020-08-11T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><em>Snippet from: <a href='https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing'>The Wikipedia Article</a>, along with my own notes</em></p><ul><li><strong>The network is reliable</strong> : Think about error-handling on network failures. Use the following patterns: Timeouts, Retries, Circuit breakers, Graceful Degradation.</li><li><strong>Latency is zero</strong> : Think about bandwidth requirements and packet loss. Use the following patterns: Small payloads, Compression, Asynchronous Communication, Perceived Progress, Timeouts, Graceful Degradation.</li><li><strong>Bandwidth is infinite</strong> : The same as above. Think explicitly about bottlenecks in Network traffic. Use the following patterns: Caching, Etags.</li><li><strong>The network is secure</strong> : Think about encryption, compliance, data risk. Understand the trade-offs with speed and simplicity. Prioritize security over the network for any sensitive data.</li><li><strong>Topology doesn't change</strong> : Think about local state, tolerance for staleness, co-ordination overheads, recovery mechanisms for split brains, inability to reach endpoints.</li><li><strong>There is one administrator</strong> : Remember to test the end-to-end flow and think through production components not controlled by you.</li><li><strong>Transport cost is zero</strong> : Think through data transfer costs in cloud environments. Monitor payload sizes.</li><li><strong>The network is homogeneous</strong> : Think about connection drops, reconnects, client timeouts. Use the following patterns: Idempotency, Eventual Consistency</li></ul>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/gene-kim-the-five-ideals/</id>
    <link href="https://vedang.me/gene-kim-the-five-ideals/"/>
    <title>Gene Kim: The Five Ideals of DevOps</title>
    <updated>2020-08-09T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><em>Snippet from: <a href='https://www.amazon.in/Unicorn-Project-Developers-Disruption-Thriving-ebook/dp/B07QT9QR41'>The Unicorn Project</a>, along with my own notes</em></p><ul><li><strong>The First Ideal</strong> : <b><i>Locality and Simplicity</i></b>. Build simple, decoupled systems that can iterate in isolation.</li><li><strong>The Second Ideal</strong> : <b><i>Focus, Flow and Joy</i></b>. Work in small batches with fast and continuous feedback.</li><li><strong>The Third Ideal</strong> : <b><i>Improvement of Daily Work</i></b>. Make sure that daily work can be done with minimum impediments. Prioritize productivity over everything else.</li><li><strong>The Fourth Ideal</strong> : <b><i>Psychological Safety</i></b>. Solving problems requires preventing problems, which requires honesty, which requires the absence of fear.</li><li><strong>The Fifth Ideal</strong> : <b><i>Customer Focus</i></b>. Build only that which actually matters to our customers.</li></ul>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/preventing-the-collapse-of-civilization/</id>
    <link href="https://vedang.me/preventing-the-collapse-of-civilization/"/>
    <title>Lazy Weekend Viewing: Preventing the collapse of Civilisation</title>
    <updated>2020-04-12T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><div class="orgtoc"></p><ul><li><a href='#orgf1a9f8c'>Executive Summary</a></li><li><a href='#orgd3d357f'>The video</a></li><li><a href='#org7481467'>Technology degrades over time</a></li><li><a href='#org4c40b16'>Thesis: Software Robustness is in decline right now</a></li><li><a href='#org942dc0d'>Thesis: Programmer Productivity is in decline right now</a></li><li><a href='#org94f1355'>If Robustness and Productivity are both declining, Software cannot be advancing</a></li><li><a href='#org1ef1881'>Complexity accelerates knowledge loss</a></li><li><a href='#orge4fe58b'>Resources:</a></div></li></ul><p><a id="orgf1a9f8c"></a></p><h1 id="executive_summary">Executive Summary</h1><p>"<a href='https://www.youtube.com/watch?v=pW-SOdj4Kkk'>Preventing the Collapse of Civilisation</a>" is an excellent talk by Jonathan Blow (of Braid fame) at DevGAMM 2019. It's inspiring and I recommend you make the time to see it.</p><ul><li>It is a myth that technology only advances. We've often seen significant backward slides in tech. (Lots of examples to illustrate this point)</li><li>Software is in decline. Both software robustness and programmer productivity is declining. (Lots of examples to illustrate this point)</li><li>We need to fight complexity and strive for simplicity in every step if we want to battle degradation and loss of capability.</li></ul><p><a id="orgd3d357f"></a></p><h1 id="the_video">The video</h1><p><div class="video-container"> <iframe src="https://www.youtube.com/embed/pW-SOdj4Kkk" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen> </iframe> </div></p><p><a id="org7481467"></a></p><h1 id="technology_degrades_over_time">Technology degrades over time</h1><ul><li>Unless people make intentional effort to avoid this.<ul><li>We have this idea of technology marching forward, but it goes backward all the time.</li><li>We've forgotten lots of tech over time when empires have fallen. (excellent examples in the talk)</li><li>Entire inter-connected thriving global system collapsed in 1177 BC. (end of the Bronze Age)</li></ul></li><li>It takes a lot of energy to communicate from generation to generation and there are losses inevitably.<ul><li>This has happened to us with Microprocessors already. (Interview with Bob Colwell, Chief Microprocessor Architect, Intel illustrating this point)</li></ul></li></ul><p><a id="org4c40b16"></a></p><h1 id="thesis%3A_software_robustness_is_in_decline_right_now">Thesis: Software Robustness is in decline right now</h1><ul><li>It's hard to recognize a slow collapse, the fall of the Roman Empire took 300 years.</li><li>Software has been free riding on hardware, so it looks like we are doing okay.</li><li>Software is having small local technological improvements (like machine learning) while suffering from degradation is other areas (like creating efficient apps)</li><li>We don't expect software to work anymore, and this has been happening for so long that it's become the new normal.<ul><li>"Just restart it!"</li><li>Jon: "I'm just going to note down / take a screenshot every time software I use has an obvious bug or unintuitive / incorrect behavior." ~ collected loads of examples in just a few days.</li><li>Try counting for yourself and you will be surprised by how many bugs there are, where the system just doesn't do the thing it's supposed to.</li></ul></li><li>If your laptop itself is less than 3 nines uptime, nothing running on it will be 5 nines. 5 nines was a standard that we were selling in the 90s, but it's completely infeasible today.</li><li>If you haven't seen an entire industry produce robust software in a decade, what makes you think they can do it at all?</li></ul><p><a id="org942dc0d"></a></p><h1 id="thesis%3A_programmer_productivity_is_in_decline_right_now">Thesis: Programmer Productivity is in decline right now</h1><ul><li>Programmers: "Working on a higher level of abstraction lets me be smart! Save Effort!"</li><li>You start out by being right and then you extrapolate it too far into the wrong territory.</li><li>The flip side of higher lever of abstraction is the loss of capability altogether.</li><li>Productivity per programmer is dropping, not increasing. This is easy to look at in terms of innovation in Products across multiple companies vs number of engineers they employ.<ul><li>Interview with Ken Thompson talking about writing Unix in 3 weeks to illustrate this point.</li></ul></li></ul><p><a id="org94f1355"></a></p><h1 id="if_robustness_and_productivity_are_both_declining%2C_software_cannot_be_advancing">If Robustness and Productivity are both declining, Software cannot be advancing</h1><ul><li>"You cannot just"<ul><li>Copy a program from one computer to another<ul><li>The installer is for getting around incompatibilities added at the OS layer. The OS removes capabilities like compatibility.</li></ul></li><li>Statically link</li><li>Draw a pixel on a screen</li><li>Write a shader</li><li>Compile a program without a manifest</li><li>Run an unsigned program</li></ul></li><li>All of these things add friction, bugs, engineering time, headspace. They keep us from thinking about and doing interesting things.</li></ul><p><a id="org1ef1881"></a></p><h1 id="complexity_accelerates_knowledge_loss">Complexity accelerates knowledge loss</h1><ul><li>An example of complication: Language Server Protocol<ul><li>A complicated, slow way to do libraries.</li><li>It's a hassle to write plugins (made up problem). So instead, just talk to a language server over a socket.</li><li>There is nothing special in looking up the location of X. It's just an API, and what you are saying is we should architect APIs like this. Which is crazy.</li><li>If you have to author something for this system, you now have to author and debug for a distributed system, where state is not located in any central place.</li><li>You are basically converting your fast system into a slow distributed debugging hell.</li></ul></li><li>Is there a point of reasonable complexity, and if so are we past it? How would we know?<ul><li>Example of how complexity will impact the gaming industry: First there won't be people to make competing Game engines, then there won't be people good enough to maintain the existing systems.</li><li>In the bronze age, reading and writing were not widespread, so they were fragile.<ul><li>Today, almost nobody knows what is happening on a CPU/GPU, that skill is fragile.</li></ul></li></ul></li><li>Complication accelerates Knowledge Loss<ul><li>More to know, so you only end up knowing a small percentage.</li><li>Deep knowledge replaced by trivia</li><li>Good information drowned by noise.<ul><li>The harder it is to understand something, the more noise / bad answers around it.</li></ul></li></ul></li></ul><p><a id="orge4fe58b"></a></p><h1 id="resources%3A">Resources:</h1><ul><li>Videos recommended by Jonathan Blow:<ul><li>Casey Muratori: <a href='https://www.youtube.com/watch?v=kZRE7HIO3vk'>The Thirty Million Line Problem</a></li><li>Samo Burja: <a href='https://www.youtube.com/watch?v=OiNmTVThNEY'>Civilization: Institutions, Knowledge and the Future</a></li><li>Eric Cline: <a href='https://www.youtube.com/watch?v=hyry8mgXiTk'>1177 BC: The Year Civilization Collapsed</a></li></ul></li></ul>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/love-letter-to-clojure/</id>
    <link href="https://vedang.me/love-letter-to-clojure/"/>
    <title>Lazy Weekend Reading: A love letter to Clojure</title>
    <updated>2019-10-13T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><div class="orgtoc"></p><ul><li><a href='#org3712fa3'>General software philosophy</a></li><li><a href='#org24730a7'>Specific Programming Practices</a></li><li><a href='#orga227713'>The Joy of Clojure</a></li><li><a href='#org3ce5cc5'>Other notes</a></li><li><a href='#org4a15d2b'>Resources:</a></div></li></ul><p>I had no idea that <em>Gene Kim</em>, author of three books that have garnered wide-spread public acclaim, was doing Clojure programming. (They are DevOps Handbook, Phoenix Project, and Accelerate &#x2013; we've ordered them recently for the Helpshift Library). He published <a href='https://itrevolution.com/love-letter-to-clojure-part-1/'>a love-letter to Clojure</a> this weekend, and I think it is an absolute must-read for <strong>all programmers</strong> (whether you write Clojure or not). The blog-post covers the following areas:</p><p><a id="org3712fa3"></a></p><h1 id="general_software_philosophy">General software philosophy</h1><ul><li>Choose tools that are good to <strong>think</strong> with</li><li>Most bugs are due to programmers not understanding all the possible states their code may execute in. <strong>Make state and it's mutation explicit</strong>. Put it in a specific place and mutate it with a small set of specific functions.</li><li><strong>Conventions matter</strong>. They help readability of code, which is critical.</li><li><strong>Make systems simple to change</strong>. Don't "trap yourself in a system of work where you can no longer solve real business problems easily. Instead, you’re forced to merely solve puzzles all day, trying to figure out how to make your small change, obstructed by your complected system".</li><li>Developer productivity is enhanced when they follow the ideals listed below. Invisible structures around us either help or hinder in this regard.<ul><li>The first ideal: <strong>Locality and Simplicity</strong></li><li>The second ideal: <strong>Focus, Flow and Joy</strong></li><li>The third ideal: <strong>Improvement of Daily Work</strong></li><li>The fourth ideal: <strong>Psychological Safety</strong></li><li>The fifth ideal: <strong>Customer Focus</strong></li></ul></li><li>"Practice as a transcendental experience" and "Practice as hard work and perseverance" are both needed for developers to level up.</li><li>Bugs can be introduced by not understanding programming language constructs (eg: in Ruby, strings are mutated if you use the << operator).</li></ul><p><a id="org24730a7"></a></p><h1 id="specific_programming_practices">Specific Programming Practices</h1><ul><li><strong>Pure functions are trivial to test</strong> &#x2013; build input, look at output. If it's hard to write a test for your code, that's a sign that it is complicated. Try to simplify it by pulling pure functions out of it and writing tests for those.</li><li><strong>Push side-effects (impure functions) to the edge of the program</strong> / piece of code that you are writing. Not practicing this leads to code becoming increasingly untestable and difficult to change.</li><li>Pushing I/O to edges reduces the need for mocks and stubs</li></ul><p>Here is an example of good vs bad way:</p><pre><code class="language-clojure">
;; good way: steps are composed together, which each compiler phase
;; indepdendently executable and testable.

&#40;-&gt; &#40;tokenize-source-files!&#41;
    &#40;generate-abstract-syntax-tree&#41;
    &#40;generate-intermediate-representation&#41;
    &#40;generate-assembly-instructions&#41;
    &#40;write-assembly-output-files!&#41;&#41;

;; bad way: all the intermediate steps buried inside other functions,
;; no longer reachable or inspectable.

tokenize-source-files-and-generate-ir-and-generate-assembly&#40;&#41;;
</code></pre><p><a id="orga227713"></a></p><h1 id="the_joy_of_clojure">The Joy of Clojure</h1><ul><li>Ease of writing short, beautiful code that just works.</li><li>Simpler syntax frees your brain to think about the problem you want to solve.</li><li>The REPL experience enables developer to stay focused and in flow state. The developer experiences joy in REPL driven development.</li><li>Clojure is a high-level language that gets out of your way. "A programming language is low-level when its programs require attention to the irrelevant." - Alan Perlis</li></ul><p><a id="org3ce5cc5"></a></p><h1 id="other_notes">Other notes</h1><ul><li>The brightest days of infrastructure, operations and security are still ahead of us.</li><li>They involve providing platforms to developers that hide the complexity of these things and allow the developer to focus on the business problem they want to solve.</li></ul><p><a id="org4a15d2b"></a></p><h1 id="resources%3A">Resources:</h1><ul><li>Some great blog-posts:<ul><li>Bryan Cantrell: <a href='https://twitter.com/bcantrill/status/1042180114199375872'>A love letter to Rust</a> (which inspired this post)</li><li>John Carmack: <a href='http://www.gamasutra.com/view/news/169296/Indepth%5C_Functional%5C_programming%5C_in%5C_C.php'>The power of functional programming concepts in C++</a></li><li>Shu Uesugi and Julien Benchetrit: <a href='https://medium.freecodecamp.org/react-introduction-for-people-who-know-just-enough-jquery-to-get-by-2019-version-28a4b4316d1a'>An Introduction to React in 2019 (For People Who Know Just Enough jQuery To Get By)</a></li><li>Uncle Bob Martin: <a href='https://blog.cleancoder.com/uncle-bob/2019/08/22/WhyClojure.html'>Why Clojure?</a></li></ul></li><li>Some great videos:<ul><li>John Carmack: <a href='https://youtu.be/1PhArSujR_A'>Rewriting Wolfenstein 3D using functional programming techniques</a></li><li>Rich Hickey: <a href='https://www.infoq.com/presentations/Simple-Made-Easy/'>Simple Made Easy</a></li><li>Dr. Mihaly Csikszentmihalyi: <a href='https://www.ted.com/talks/mihaly_csikszentmihalyi_on_flow?language=en'>TED talk on the flow state</a></li><li>Brian Goetz: <a href='https://youtu.be/2y5Pv4yN0b0'>Stewardship of the Java ecosystem</a></li><li>Rafal Dittwald: <a href='https://youtu.be/vK1DazRK_a0'>Solving problems the Clojure way</a></li><li>Bruce Hauman: <a href='https://youtu.be/j-kj2qwJa_E'>Talking about figwheel for Clojurescript development</a></li><li>Sean Corfield: <a href='https://www.youtube.com/embed/UFY2rd05W2g'>Using the REPL to debug a problem in core.memoize</a></li><li>Tiago Luchini: <a href='https://youtu.be/EDojA_fahvM'>Writing declarative domain models</a></li><li>David Nolen: <a href='https://www.youtube.com/watch?v=77b47P8EpfA'>Rethinking Identity</a></li></ul></li><li>Some great books:<ul><li>Michael Nygard: <code>Release it!</code></li><li>Chas Emerick, Brian Carper, Christophe Grand: <code>Clojure Programming</code></li><li>Russ Olsen: <code>Getting Clojure</code></li><li>Brian Goetz: <code>Java Concurrency in Practice</code></li><li>Martin Fowler: <code>Refactoring: Improving the design of existing code</code></li><li>Dr. Mihaly Csikszentmihalyi: <code>Flow: The psychology of optimal experience</code></li><li>Daniel Higgenbottom: <code>Clojure for the brave and true</code></li><li>Carin Meier: <code>Living Clojure</code></li><li>Mark Schwartz: <code>Seat at the table: IT leadership in the age of agility</code></li></ul></li><li>Clojure tutorials:<ul><li>Eric Normand: <a href='https://purelyfunctional.tv'>Purely Functional</a></li><li>Arne Brasseur: <a href='https://lambdaisland.com'>Lambda Island</a></li></ul></li></ul>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/writing-a-good-status-update/</id>
    <link href="https://vedang.me/writing-a-good-status-update/"/>
    <title>Writing a good status update</title>
    <updated>2019-08-06T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><div class="orgtoc"></p><ul><li><a href='#org537fb11'>What is the purpose of status updates?</a></li><li><a href='#orgce655a4'>Properties of good status updates</a></li><li><a href='#orgc8ba31b'>Writing the status update</a></li><li><a href='#orgecda601'>Create sections for your updates</a><ul><li><a href='#org8472657'>Name the project you are currently working on</a></li><li><a href='#org7d88d63'>Name the milestone of the project you are currently working on.</a></li><li><a href='#org82aa969'>Your status update should definitively answer the following questions:</a></li><li><a href='#org06d13ca'>Example status update</a></li></ul></li><li><a href='#org9d7cc79'>What do you get from writing a good update?</a></div></li></ul><p>We share written status updates within the team on a weekly basis (every Monday). I wrote this article to explain what these status updates should look like, and I think it's useful enough to publish publicly. Here goes:</p><p><a id="org537fb11"></a></p><h1 id="what_is_the_purpose_of_status_updates%3F">What is the purpose of status updates?</h1><ul><li>Let <strong>your manager</strong> know what you have achieved last week.</li><li>Let <strong>your co-workers</strong> know what you have achieved this week.</li><li><strong>Reflect</strong> on your progress and pace <strong>yourself</strong>.</li></ul><p><a id="orgce655a4"></a></p><h1 id="properties_of_good_status_updates">Properties of good status updates</h1><ul><li><strong>Short</strong> and <strong>meaningful</strong>. (5 min reading time)</li><li>Communicate <strong>work in the previous week</strong>, <strong>highlight progress</strong>.</li><li>Communicate <strong>plan for the next week</strong>, <strong>help make yourself efficient</strong>.</li><li>Communicate <strong>open questions</strong> and <strong>blockers</strong>, <strong>highlight areas where we need to help you</strong>.</li></ul><p><a id="orgc8ba31b"></a></p><h1 id="writing_the_status_update">Writing the status update</h1><p>Write the status update either as the last thing on Friday or as the first thing on Monday. Remember to review the status you had posted previously, so that you don't miss out on any updates. If you choose to write on Friday end-of-day, this can also be a great wind-down / logging-off ritual to wrap up the week and give yourself a sense of accomplishment.</p><p><a id="orgecda601"></a></p><h1 id="create_sections_for_your_updates">Create sections for your updates</h1><ul><li>Use the following sections:<ul><li>What I accomplished this last week</li><li>What didn't go according to plan</li><li>What I plan to do next week</li><li>Questions / Blockers / Action Items</li></ul></li><li>This makes it <strong>easy to parse</strong> your update.</li></ul><p><a id="org8472657"></a></p><h2 id="name_the_project_you_are_currently_working_on">Name the project you are currently working on</h2><ul><li>Yes, your manager is supposed to know what you are working on. But you often do unplanned, extra tasks too. You help on other projects, handle production issues, discover interesting tidbits, get insights from analyzing logs. <strong>Highlight all of these activities!</strong>.</li><li>Help your manager by making it easy for them to compile project reports.</li><li>This makes your update very readable.</li></ul><p><a id="org7d88d63"></a></p><h2 id="name_the_milestone_of_the_project_you_are_currently_working_on.">Name the milestone of the project you are currently working on.</h2><ul><li>Helps when the team needs to take a call about cutting or increasing scope on a project.</li><li>Highlights and reinforces upcoming co-ordination points.</li></ul><p><a id="org82aa969"></a></p><h2 id="your_status_update_should_definitively_answer_the_following_questions%3A">Your status update should definitively answer the following questions:</h2><ul><li>What are you working on?</li><li>For each project that you are working on, where are you? What is the next date that you are committing to?</li><li>Are you on track for the current phase of the project or not? If not, what is the impact on the overall project?</li></ul><p><a id="org06d13ca"></a></p><h2 id="example_status_update">Example status update</h2><h3 id="what_i_accomplished_this_last_week%3A">What I accomplished this last week:</h3><ul><li>Completed final PRD review for <strong>Project X</strong>, no open questions at this point! :yay:. Project is <strong>on track</strong>!</li><li>Pushed a fix to <strong>ABC Service</strong> to production and closed <strong>Jira Ticket Z</strong>. Graphs show amazing reduction in network bandwidth! :epicwin: <em>link to graph or screenshot</em>.</li><li>Conducted the Enterprise initiative meeting, meeting notes are here: <em>link to meeting notes</em>.</li></ul><h3 id="what_didn%27t_go_according_to_plan%3A">What didn't go according to plan:</h3><ul><li>Working on dev of <strong>Project Y</strong>. Project is <strong>not on track</strong> :sad<sub>face</sub>:. We had communicated that dev will be complete by <span class="timestamp-wrapper"><span class="timestamp">&lt;2019-08-02 Fri&gt; </span></span> but I will need 3 more days. This pushes the <strong>new date to <span class="timestamp-wrapper"><span class="timestamp">&lt;2019-08-07 Wed&gt;</span></span></strong> EOD.</li></ul><h3 id="what_i_plan_to_do_next_week%3A">What I plan to do next week:</h3><ul><li>I will have the initial estimates for <strong>Project X</strong> by EOD <span class="timestamp-wrapper"><span class="timestamp">&lt;2019-08-08 Thu&gt;</span></span>.</li><li>I will complete the Dev work on <strong>Project Y</strong> as mentioned above. <strong><span class="timestamp-wrapper"><span class="timestamp">&lt;2019-08-07 Wed&gt; </span></span> EOD</strong></li><li>I will create a document about <strong>DEF Topic</strong> as we had discussed by EOD <span class="timestamp-wrapper"><span class="timestamp">&lt;2019-08-09 Fri&gt;</span></span>.</li></ul><h3 id="questions_%2F_blockers_%2F_action_items%3A">Questions / Blockers / Action Items:</h3><ul><li>We need to sync up about ideas for implementing <strong>Project X</strong> before tomorrow EOD, but you don't have an empty meeting slot until day-after. What should we do?</li><li>I will inform <strong>Frontend team-member</strong> that <strong>Project Y</strong> integration testing cannot start tomorrow, instead it will start on <span class="timestamp-wrapper"><span class="timestamp">&lt;2019-08-08 Thu&gt;</span></span>.</li></ul><p><a id="org9d7cc79"></a></p><h1 id="what_do_you_get_from_writing_a_good_update%3F">What do you get from writing a good update?</h1><ul><li>A <strong>record of work for yourself</strong>. This is invaluable come performance review time. You only have to read through 26 updates and you have a thorough summary.</li><li><strong>Help your manager</strong> advocate for you during appraisals. You should care about your career more than anyone else.</li><li><strong>Help yourself</strong>. Writing the update should <strong>give you</strong> a clear idea of your own progress and what you need to work on.</li></ul>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/gojek-10x-engineering/</id>
    <link href="https://vedang.me/gojek-10x-engineering/"/>
    <title>Lazy Weekend Viewing: GOJEK's 10x Engineer - Truth or Myth?</title>
    <updated>2019-07-20T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><div class="orgtoc"></p><ul><li><a href='#org0d8a8b6'>Executive Summary</a></li><li><a href='#org8076cdb'>The video</a></li><li><a href='#org122b20a'>Introductions</a></li><li><a href='#org2f90d0f'>How do you get so much done with so few engineers</a></li><li><a href='#org8e9ae08'>Collaboration, Communication and Team Size</a></li><li><a href='#org51ff81b'>What about "number of pods"? Will infinite teams of 5 scale infinitely?</a></li><li><a href='#orge1acd86'>Complexity of organisations</a></li><li><a href='#org2eec0d4'>Monolith to SOA</a></li><li><a href='#orgcd71466'>Relationship as a service AKA How politics enters your system</a></li><li><a href='#orgb175c5d'>Is there such a thing as a 10x engineer?</a></li><li><a href='#org351e4de'>Good practices in coding</a></li><li><a href='#orgc854b60'>Differences between good engineers and 10x engineers</a></li><li><a href='#org6da9ca1'>How do you spot red flags in a new hire?</a></li><li><a href='#org06acc0a'>Onboarding / Engineering bootcamp</a></li><li><a href='#org8a3e3e6'>Gojek culture</a></div></li></ul><p><a id="org0d8a8b6"></a></p><h1 id="executive_summary">Executive Summary</h1><ul><li>Engineering quality is paramount. Focus on <a href='https://dave.cheney.net/2019/07/09/clear-is-better-than-clever'>clean and clear code</a>. Code is the primary medium of communication for any engineer. Write beautiful code and hold people to high standards.</li><li>Adding head count has vast hidden costs and often brings down output. The reason for this is the exponential increase in communication required to align everyone to common goals.</li><li>Similarly, pods cannot scale unless they can own small, independent components.</li><li>Monoliths are normal and good when the company is small. Refactoring the monolithic model into different components allows us to scale pods and org.</li><li>Carve monoliths only when it hits critical mass. Then, identify the pain and pull it out into it's own pod.</li><li>"Relationship as a service" or "Please get this done for me over and above other stuff on your plate"<ul><li>"Traffic congestion" (A team which is too busy) and structural failure can lead to the impression that the system is bureaucratic and political.</li></ul></li><li>Criteria when hiring Engineers<ul><li>Computer science (Can computers understand your code?)<ul><li>Ability to grasp large complex systems</li><li>Understanding of implications of design choices on that system.</li></ul></li><li>Software Engineering (Can humans understand your code?)<ul><li>How well do you communicate through your code? This is the metric that enables building <strong>good</strong> systems.</li><li>The hygiene you show in code is the hygiene you will enforce on others.</li></ul></li><li>Good behavioral traits (Can you grow?)<ul><li>Curious</li><li>Wants to learn</li><li>Can accept feedback</li><li>"Strong opinions, Weakly held."</li><li>"Pride without attachment."</li></ul></li></ul></li><li>Find the full video <a href='https://www.youtube.com/watch?v=He0XBBfCEVk'>here</a>!</li><li>The rest of this post is detailed notes on the video</li></ul><p><a id="org8076cdb"></a></p><h1 id="the_video">The video</h1><p><div class="video-container"> <iframe src="https://www.youtube.com/embed/He0XBBfCEVk" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen> </iframe> </div></p><p><a id="org122b20a"></a></p><h1 id="introductions">Introductions</h1><ul><li>CEO: Nadiem Makarim</li><li>CTO: Niranjan Paranjape</li><li>Tech Recruitment Head: Sidu Ponnappa</li><li>C42 Engineering: Sequoia SWAT Team for solving tech problems<ul><li>They were the rare "other Clojure shop" in India in 2010.</li></ul></li><li>Common consulting milestone for them was "the Client just tried to poach me". When Gojek tried this with Niranjan, they sold the entire company to Gojek.</li><li>Today, Gojek does $9 BILLION in transactions with 300 engineers.</li></ul><p><a id="org2f90d0f"></a></p><h1 id="how_do_you_get_so_much_done_with_so_few_engineers">How do you get so much done with so few engineers</h1><ul><li>Adding headcount does not increase output.</li><li>With every engineer you add, you actually slow down work (exponentially)</li><li>A good finance person will challenge you on every item of spend because they understand that that's how they lose money.</li><li>Similarly, a good engineering leader will challenge you on every addition of headcount. The hidden cost is enormous and impacts ability to deliver quickly and at quality.</li></ul><p><a id="org8e9ae08"></a></p><h1 id="collaboration%2C_communication_and_team_size">Collaboration, Communication and Team Size</h1><ul><li>Every person you add exponentially increases the combinatorics of communication you need in order to agree on what exactly it is that you are building.</li><li><strong>Code is communication between humans. Keep it clean and simple.</strong></li><li>Upto 4 people, productivity grows linearly with addition of people. Beyond that it starts plateauing and dipping.<ul><li>The person who observed this started slicing and dicing teams so that at any point in time a subsystem was being worked on by no more than 4 people.</li><li>By <a href='https://goodroot.ca/post/2018/10/13/practicality-metaphysics-conways-law/'>Conway's Law</a>, he was forcing system architecture to be set up in a certain way.</li></ul></li><li>We chose to map out our systems based on what a 5 person team could do.</li></ul><p><a id="org51ff81b"></a></p><h1 id="what_about_%22number_of_pods%22%3F_will_infinite_teams_of_5_scale_infinitely%3F">What about "number of pods"? Will infinite teams of 5 scale infinitely?</h1><ul><li>As an organization, we find similar diminishing returns with growing the number of pods.</li><li>The company's ability to add pods is also limited by ability of the pods to understand the underlying system.</li><li>If your pods are independent (have to collaborate only once in 2 to 4 weeks), then you are in a manageable situation.</li><li>Multiple pods working on the same problem leads to pushing for local goals (evolving the system to maximize your objectives based on limited understanding)</li></ul><p><a id="orge1acd86"></a></p><h1 id="complexity_of_organisations">Complexity of organisations</h1><ul><li>"Don't slow me down" from CEO to engineering<ul><li>Philosopher poet engineers</li><li>Engineering had to educate the CEO on the bad effects of not on-boarding new comers fast enough</li></ul></li><li>Adding Kafka to our stack allowed us to scale<ul><li>Helped us decouple communication</li><li>Tap into our data, you don't need to ask me anymore.</li></ul></li><li><strong>Refactoring the monolithic model into different components allowed us to put more people on the independent teams.</strong></li></ul><p><a id="org2eec0d4"></a></p><h1 id="monolith_to_soa">Monolith to SOA</h1><ul><li>We first pulled out the allocation system<ul><li>matching driver to customer</li></ul></li><li>Next we pulled out Go Pay<ul><li>This allowed us to staff the team of Go Pay without them having to understand the entire monolith.</li></ul></li><li><strong>Identify a problem, pull it out, establish a pod around it.</strong></li><li>CTO was pushing CEO:<ul><li>Before we move into this new thing, we need to get rid of the monolith first.</li><li>The choice they made was <strong>never stall growth</strong>. This meant hanging on to the monolith for longer than they would have liked, and refactoring it in bits and pieces rather than in one single effort.</li></ul></li><li>When starting out, it makes complete sense to have a monolith.</li><li>When you carve a system out of a monolith, eventually that will become a monolith.<ul><li>Now you need to split it again.</li><li><strong>Identify when the monolith reaches critical mass, and then refactor your team and code-base in lockstep.</strong></li></ul></li></ul><p><a id="orgcd71466"></a></p><h1 id="relationship_as_a_service_aka_how_politics_enters_your_system">Relationship as a service AKA How politics enters your system</h1><ul><li>I need something from your system, so I'm going to lean on our relationship to unblock me and ignore your own priorities.</li><li>"Hey I really need this done fast, because X".</li><li>This will grow until you are completely busy trying to unblock other people and are unable to focus on any of your own priorities.</li><li>Now you have a bureaucracy. The person with the back-channel to the blocked system gets to jump the queue. This inherently does not scale because people without the relationship back-channel get more and more frustrated.</li><li>"Our organization is political." I don't have privileged access, therefore that other guy must be master politician.</li><li><strong>The actual underlying problem is traffic congestion.</strong><ul><li>If you are trying to do your best, you are going to use every resource at your disposal, including your relationships, to get work done</li></ul></li><li><strong>Even in the most well-intentioned and non-egotistical organizations, structural failure leads to the perception of favouritism</strong></li></ul><p><a id="orgb175c5d"></a></p><h1 id="is_there_such_a_thing_as_a_10x_engineer%3F">Is there such a thing as a 10x engineer?</h1><ul><li><strong>As system complexity grows, for you to meaningfully contribute, you need to know the system!</strong></li><li>Criteria to select engineers<ul><li>Computer science (can computers understand your code?)<ul><li>Ability to grasp large complex systems</li><li>Understanding of implications of design choices on that system.</li></ul></li><li>Software Engineering (can humans understand your code?)<ul><li>How well do you communicate through your code?</li><li>Communication problems are the hard blockers on building good systems.</li><li>Well written code provides contextual logic and <a href='https://dave.cheney.net/2019/07/09/clear-is-better-than-clever'>clarity</a>.</li></ul></li><li>Good behavioral traits:<ul><li>curious</li><li>wants to learn</li><li>reads books</li><li>can accept feedback</li><li>^ This is the kind of person who can grow.</li></ul></li></ul></li></ul><p><a id="org351e4de"></a></p><h1 id="good_practices_in_coding">Good practices in coding</h1><ul><li>Every code-base comes with a glossary</li><li>Terms have specific meaning, and naming is critical in the dividends it pays off in the future.</li><li>Every engineer at every level has to take a written test</li><li>We are not looking at whether you can solve the complex problem. We are judging how you express yourself. You are a programmer, and your code should speak well for you.</li><li><strong>Hygiene becomes more and more important as a programmer becomes more and more senior.</strong><ul><li>The hygiene you show in code is the hygiene you will enforce on others.</li></ul></li><li>High standards are important.</li></ul><p><a id="orgc854b60"></a></p><h1 id="differences_between_good_engineers_and_10x_engineers">Differences between good engineers and 10x engineers</h1><ul><li>10x <strong>outcome</strong> (not output)</li><li>As you grow your organization, individual outcome is less important. Team outcome is what you need to measure.</li><li>Engineering is completely a team sport.</li><li>You can have deeply capable and extremely competent individual contributors who like to work alone. As long as their code fits beautifully with other systems, they are multiplying everyone's capacity.</li></ul><p><a id="org6da9ca1"></a></p><h1 id="how_do_you_spot_red_flags_in_a_new_hire%3F">How do you spot red flags in a new hire?</h1><ul><li>Every good engineer is highly opinionated. A good engineer has strong opinions weakly held.<ul><li>You should be able to change your opinion on being presented facts.</li></ul></li><li>If you are unable to deal with criticism of your code, that's a smell.<ul><li>We ask junior people to interview senior engineers. This shows the grace they have when dealing with others.</li></ul></li><li>We routinely wind up in situations where you have young people owning complex systems. Senior engineers need to be hired to now deal with these systems, and they will be on-boarded by juniors. They should be able to deal with this fact.</li><li>CTO: "When I lay out a design, I expect people to give me rational criticism so that I can learn from them."</li><li>If you had to hire someone with deep experience vs great behavioral foundations, what would you choose?<ul><li>It Depends!</li><li>Sometimes, you need to bring deep experience to the table to build something out and learn from the guru.</li><li>Behavioral traits means that someone is teachable. Outside the Valley, this is extremely important because of the lack of access to experienced engineering.</li></ul></li></ul><p><a id="org06acc0a"></a></p><h1 id="onboarding_%2F_engineering_bootcamp">Onboarding / Engineering bootcamp</h1><ul><li>Structured decision making</li><li>Basic coding hygiene</li><li>Collaboration<ul><li>How do you engage with someone else to meaningfully decide reasonably quickly what something in code should be.</li></ul></li><li>Heavy focus on coding during the bootcamp.</li><li>We bring in coaches from <strong>every team</strong> and teach. (regardless of hire). This helps us build empathy, which is critical for future collaboration.</li><li>Pride without attachment.<ul><li>Pride allows you to focus on creating beautiful code.</li><li>Attachment means you will stand in the way of the evolution of your beautiful baby. You need to let it go and let others change it.</li><li>If you look back at code you wrote 6 months back and don't notice how bad you were, you aren't growing.</li></ul></li><li>Repeat the same problem every Monday throughout the whole bootcamp. Ask them to review every week and see if they find that feeling of progress.</li></ul><p><a id="org8a3e3e6"></a></p><h1 id="gojek_culture">Gojek culture</h1><ul><li>Even the most high-performing team has to deal with intense time pressure.</li><li>What makes or breaks teams under pressure?<ul><li>Show them the impact that they are making.</li><li>You don't need to push engineers, you need to give them the rush! External force will not drive the maker as much as the impact of work will.</li><li>The value you create is not proportional to the time you put in, it's proportional to the state of your mind in that time you put in.<ul><li>If you are burned out, or your team mate is burned out then there is going to be a huge negative spiral.</li></ul></li><li>To harness creativity, you need balance and psychological safety. The hours that you put in, you need to be in a state of flow. This requires you to take short breaks after days of intense activity.</li></ul></li><li>You can have a 10x engineer amidst you, but you may never know it because you did not give them sufficient leeway to unleash their art.</li></ul>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/problems-in-system76-oryx-pro/</id>
    <link href="https://vedang.me/problems-in-system76-oryx-pro/"/>
    <title>Problems in my brand new System76 Oryx Pro laptop</title>
    <updated>2019-05-31T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><div class="orgtoc"></p><ul><li><a href='#org9486a58'><strong>Important Note</strong></a></li><li><a href='#orga97b315'>Emacs is unusable due to constant movement of the cursor.</a></li><li><a href='#org6850a29'>The space key is weird.</a></li><li><a href='#org419f794'>Power management and suspend.</a></div></li></ul><p><a id="org9486a58"></a></p><h1 id="**important_note**"><strong>Important Note</strong></h1><p>I'm enjoying using a Linux laptop after years of development on the Macbook, and this post is more a list of issues I personally faced on moving to Linux after 7 years of development on the Macbook. I'd written a similar thing when I'd moved to the Macbook for the first time. Hopefully, this will help someone else moving to this particular laptop. This is not a "look, linux sux" post. The Oryx Pro is a great machine, and is a far better experience than the overall Linux experience I remember from 2012.</p><p><a id="orga97b315"></a></p><h1 id="emacs_is_unusable_due_to_constant_movement_of_the_cursor.">Emacs is unusable due to constant movement of the cursor.</h1><ul><li>The cursor constantly moves to the end of the first line on screen for no apparent reason.<ul><li>I'm convinced the reason that the cursor moves to the end of the first line on screen has to do with the position of the mouse-pointer. I confirmed this by changing &#8216;mouse-avoidance-mode' to &#8216;animate' (from &#8216;banish'). This stops the pointer from being moved to the top right corner on key press in Emacs. Now, the cursor does not move to the end of the first line like it used to, but to some other random place (wherever the mouse pointer happened to be last)</li></ul></li><li>Repeatedly performing certain key-bindings (like C-n) causes a buffer menu to pop up. I have no idea why this happens and how to reproduce it / stop it.<ul><li>Once the menu shows up, repeatedly pressing the same key-binding causes it to go away after some time. (This may be true only of C-n, which is what triggers this for me the most.)</li></ul></li><li><strong>Update</strong> : Both problems above are due to the awkward placing of the touchpad on the System 76 laptops. This touchpad is sensitive and registers "ghost clicks" during typing. I confirmed this by disabling my touchpad. The problem went away. This is an acceptable workaround for me as I don't need the mouse when I'm in Emacs (and disabling/enabling the touchpad is straightforward using <code>xinput</code>)<ul><li><code>xinput list</code> will list all the available input devices. Look for your touchpad and note the id of the device.</li><li><code>xinput disable &lt;id&gt;</code> will disable the device.</li><li><code>xinput enable &lt;id&gt;</code> will enable the device.</li></ul></li></ul><p><a id="org6850a29"></a></p><h1 id="the_space_key_is_weird.">The space key is weird.</h1><ul><li>More than any other key on the laptop, the space key needs a solid thump for it to register. This is especially irritating when typing out passphrases, since there is no visible feedback.</li><li><strong>Update</strong> : Over time, I've gotten used to this and no longer need to worry about whether my keypress has registered or not.</li></ul><p><a id="org419f794"></a></p><h1 id="power_management_and_suspend.">Power management and suspend.</h1><h2 id="no_%22just_works%22_for_suspend.">No "just works" for Suspend.</h2><ul><li>Years of using the Mac have trained me to just shut the laptop lid when I'm done working. However, with Oryx, there is no predictable way to tell whether the battery will continue to be drained or not once the lid is down. I've left a fully charged laptop, come back to it 4 hours later to find that it was completely drained (to the point that it does not start unless you plug it into a power source). I have no clue why this happens.<ul><li><strong>Update</strong> : Apparently this is famous in the world of System76 as the 'suspend black screen of death' bug. :( These are the kind of things due to which 'year of the Linux desktop' is a broken promise.</li></ul></li><li><strong>Update 2</strong> : Suspend works okayish, it just drains the battery faster than I would have expected. I guess I'll have to learn to live with this (but it seriously hampers the use of this laptop as a carry-around device, which is something I'm grumpy about).</li><li><strong>Update 3</strong> : Prolonged use of this laptop really makes me appreciate the incredible power management of the Macbook. I love Oryx as a development environment, but the Macbook's power management is in a whole different class.</li></ul><h2 id="the_power_button_is_weird.">The Power button is weird.</h2><p>I've gotten into the habit of shutting down my laptop when I'm unsure about battery and want to definitely conserve it (example when I'm traveling). There is no immediate feedback on pressing the power button. Due to this, it's hard to know if the keypress has registered or not. This is a small gripe in the big picture, but it gets annoying fast.</p>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/simulating-the-passport-seva-kendra/</id>
    <link href="https://vedang.me/simulating-the-passport-seva-kendra/"/>
    <title>Simulating the Passport Seva Kendra using Clojure</title>
    <updated>2018-12-15T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><div class="orgtoc"></p><ul><li><a href='#org1c298fd'>The Problem Statement</a></li><li><a href='#org12e4ecc'>So what is the problem with the system?</a></li><li><a href='#org70af8c3'>Representing all the information about the PSK</a></li><li><a href='#org768ca2e'>Letting people into the PSK, and generating token numbers for them.</a></li><li><a href='#org8529d82'>Queuing up people and simulating the work done at every stage</a></li><li><a href='#org0d27fed'>Keeping track of people and the display board</a></li><li><a href='#orgd9b0da0'>Processing people concurrently across all open counters</a></li><li><a href='#org02aab93'>Tying everything together - the main function</a></li><li><a href='#orgedd2c48'>With me so far? Some thoughts</a></li><li><a href='#org4a50143'>Can we see the problem?</a></li><li><a href='#org96cb052'>Final Thoughts</a></li><li><a href='#org3f50c59'>References</a></div></li></ul><p><em>Cross-posted from my post on the <a href='https://medium.com/helpshift-engineering/simulating-the-passport-seva-kendra-using-clojure-fd88c12dde8c'>Helpshift Engineering Blog</a></em></p><p>A year ago, I went to the Passport Seva Kendra (PSK) in Mundhwa, Pune to get my passport renewed. At the time, the government had revamped this process and made it a simple, step-in/step-out painless affair. Unfortunately for me, I hit an edge-case in the system and took much longer than expected to complete. I was there for close to 4 hours. I used this time to observe the behavior of the PSK and think about ways to improve the applicant experience. I thought it was an interesting problem to solve and write about.</p><p>Recently, my wife booked an appointment at the PSK to renew <span class="underline">her</span> passport and this provided the spark I needed to write about it. So here we are, a year later, talking about the passport renewal process at the PSK.</p><p><a id="org1c298fd"></a></p><h1 id="the_problem_statement">The Problem Statement</h1><p>Let me describe the process to you first.</p><ul><li>The PSK has appointment slots every 15 minutes, and there are ~25 people in each slot.</li><li>Once you enter the PSK, there are 4 to 6 counters to verify your documents.</li><li>On verification, you are assigned a unique token number. We'll talk about this in a bit.</li><li>Token numbers are displayed on an electronic display-board. The board indicates which counter the person should go to. You are to wait in the waiting area and look at the display. You will soon be scheduled against a counter, where a PSK employee will help you with that particular stage of the process.</li><li>There are 3 stages in the process. The first is 'Biometrics' (Stage A). At this counter, the PSK employee collects your fingerprints and takes your photo. Your online form is updated with this information. There are 36 counters serving this stage (A-1, A-2 &#x2026; A-36).</li><li>The second stage is 'Form Check' (Stage B). At this point, the PSK employee checks the details in your form. If he finds any problems, he will redirect you to the Corrections counter. During my visit, counters A-34, A-35 and A-36 were reserved for corrections. After corrections, you have to come back to this stage. There are 12 counters (B-1, B-2 &#x2026; B-12).</li><li>The final stage is 'Form Re-Check' (Stage C). At this point, the agent double-checks the work of the previous counters and takes your form. If there are any corrections you have to go back to the corrections counters and start again. Once the form is checked, your reissue request has been processed. You are free to leave the PSK and go home. There are 10 counters (C-1, C-2 &#x2026; C-10).</li><li>Coming back to the token numbers. These are of the form N-10, S-4 etc. The alphabet represents the applicant category. These categories are as follows:<ul><li><strong>Normal (represented by N):</strong> Most people fall into this category.</li><li><strong>Senior (represented by S):</strong> For people older than 60</li><li><strong>Tatkal (represented by T):</strong> For people who want speedy processing of their passport (and have paid extra for this benefit)</li><li><strong>Requiring Police Clearance (represented by P):</strong> People who need clearance from the police (probably because they have criminal records, or work in sensitive departments in the government).</li></ul></li><li>Categories other than 'Normal' have a higher priority when it comes to processing their applications. For the purposes of this post, I assume that P has the highest priority, followed by S, T and N.</li></ul><p><img src="/assets/images/psk-renderkid.jpg" alt="img" title="The workings of the Passport Seva Kendra. Illustration by Bhargava Chowdary (@renderkid />")</p><p>In the rest of this post, we'll build this system as described above, and see if we can fix the flaw in it. We will use Clojure to write the solution. Clojure's concurrency primitives are fantastic, and helped me model this system in an elegant and readable way. As we go along, I'll explain these primitives in brief. <a href='https://twitter.com/ericnormand'>Eric Normand</a> has written an excellent guide to <a href='https://purelyfunctional.tv/guide/clojure-concurrency/'>understanding concurrency primitives</a> in Clojure, and <a href='https://twitter.com/richhickey'>Rich Hickey</a> has a <a href='https://www.youtube.com/watch?v=nDAfZK8m5_8'>great talk about this</a> as well. I highly recommend both these resources to the interested reader. The focus of this post will be on using these tools to implement a non-trivial system.</p><p><a id="org12e4ecc"></a></p><h1 id="so_what_is_the_problem_with_the_system%3F">So what is the problem with the system?</h1><p>First, let's get the flaw out of the way. The problem with this system is that <span class="underline">the applicant has no idea when he'll be scheduled with an agent</span>. He must keep staring at the display board. For example, if you are N-30 and stage A took a particularly long time for you, others have moved past you to the next stage (B and beyond). The display board may read that N-41, N-42 etc are at counters B-1, B-2 etc. However, this does not mean that N-30 will show up next. The display board may go all the way to N-60 before N-30 shows up. As there is no certainty about when your number will show up, you have no option but to continuously stare at the board. This gets really irritating after a while.</p><p>Here are some ways to deal with this problem:</p><ol><li>Instead of using simple queues between the stages, use a priority queue. This means that even if N-30 took a long time on stage A, and the board had moved on to N-40s and above for stage B, as soon as N-30 is done with stage A he will be moved to the top of the queue for B. In this way, the applicant can look at the board and always tell whether he'll be up next or not.</li><li>Generate new token numbers between each stage. This will give the applicant a clear idea of the number of people ahead of him at any given point in time.</li><li>Provide the person with a new display board, where he can enter his token and see where he is in the queue for his stage.</li></ol><p>We will see these in action in our simulation program.</p><p><a id="org70af8c3"></a></p><h1 id="representing_all_the_information_about_the_psk">Representing all the information about the PSK</h1><p>Everything describing the behaviour of the PSK can be captured in code. For our simulation, the data looks like this:</p><pre><code class="language-clojure">&#40;def stages
  &quot;The various stages in the PSK, and transition from one stage to the other.

  - `processing-time-range` represents the amount of time spent at the
  counter in this stage &#40;in mins&#41;.
  - `next` represents the next stage for the person.
  - `counters` represent the number of counters/agents serving this stage.&quot;

  ;; Actual Values:
  ;; 4 Doc verification Counters
  ;; 33 counters for Biometrics
  ;; 12 for form checking
  ;; 10 for final checking
  ;; 3 for corrections
  ;; Using different values here so that the display board is
  ;; human-readable.

  {::enter {:next ::doc-verification}
   ::doc-verification {:next ::biometrics
                       :counters 10
                       :display-str &quot;0-&quot;
                       :processing-time-range &#91;1 5&#93;}
   ::biometrics {:next ::form-check
                 :counters 4
                 :display-str &quot;A-&quot;
                 :processing-time-range &#91;3 15&#93;}
   ::form-check {:next ::final-check
                 :counters 3
                 :display-str &quot;B-&quot;
                 :processing-time-range &#91;2 4&#93;
                 :failure ::corrections}
   ::final-check {:next ::exit
                  :counters 2
                  :display-str &quot;C-&quot;
                  :processing-time-range &#91;2 4&#93;}
   ::corrections {:next ::form-check
                  :counters 1
                  :display-str &quot;D-&quot;
                  :processing-time-range &#91;5 15&#93;}}&#41;

&#40;def waiting-room-capacity 500&#41; ; max number of waiting people

&#40;def total-capacity
  &quot;number of people that can be in the PSK at max&quot;
  &#40;apply + waiting-room-capacity &#40;keep :counters &#40;vals stages&#41;&#41;&#41;&#41;

&#40;def processing-batch-size
  &quot;no of people entering the center at one time.&quot;
  25&#41;

&#40;def new-batch-in-mins
  &quot;time between one batch and the next&quot;
  15&#41;

&#40;def stage-status
  &quot;For the given stage, the map of possible states that a person can be
  in.&quot;
  &#91;;; Person is in the waiting area, looking at the display to see
   ;; when he's called to a counter.
   ::waiting
   ;; Counter is assigned to the person
   ::in-process
   ;; Person is done with the counter and heading to the next stage.
   ::done&#93;&#41;
</code></pre><p>We use a simple map - <code>stages</code> - to represent all the stages in the PSK, the number of counters per stage, the amount of time per stage and the next stage after this one. A person is either waiting (looking at the display board), or is at a counter, or is done with a stage (<code>done</code> triggers a move to the next stage). From the point of view of the person, he is either waiting or at a counter.</p><p><a id="org768ca2e"></a></p><h1 id="letting_people_into_the_psk%2C_and_generating_token_numbers_for_them.">Letting people into the PSK, and generating token numbers for them.</h1><p>The first thing we will build is the token generator, and we'll use the most intuitive Clojure concurrency primitive for this: the <code>atom</code>.</p><h2 id="clojure_concurrency_primitive_-_atoms">Clojure Concurrency Primitive - Atoms</h2><p>Atoms are useful when reading / writing a single piece of data (potentially across multiple threads). This is the common case for shared resources and atoms are what I've used in most of the concurrency code I've written.</p><pre><code class="language-clojure">&#40;def token-generator
  &quot;Give the next token number to the applicant.&quot;
  &#40;atom {:normal 0
         :senior 0
         :tatkal 0
         :police-clearance 0}&#41;&#41;

;; Use the token generator to get the next token as follows:
&#40;comment
  &#40;get &#40;swap! token-generator
              update
              person-type
              inc&#41;
       person-type&#41;&#41;
;; swap! is a way to atomically change the value held by the atom.
;; This does an internal compare and set operation, and thus the function
;; passed in to manipulate the value - in this case update - may be
;; called multiple times. This function should be free of side effects.
</code></pre><p>Clojure provides something more powerful - the <code>ref</code> - when you want to deal with multiple pieces of data that change together. We'll see it in action in the following sections.</p><p>If we choose some weights to represent the probability of an applicant belonging to a certain category, we can write some code to randomly generate applicants. The relevant code is <a href='https://gist.github.com/vedang/969a726e1f49f5fc550268a22c4e4b0d#file-psk-clj-L87-L131'>here</a>. We now have a way to assign increasing token numbers to each new person entering the PSK.</p><h2 id="clojure_concurrency_primitive_-_futures">Clojure Concurrency Primitive - Futures</h2><p>We'll use another Clojure concurrency primitive - a <code>future</code> - to continuously move people into the PSK. A future object invokes the body provided to it in a different thread.</p><p>In this case, we are starting an endless loop in a new thread. This code creates some people (representing people entering the PSK), sleeps for a bit, then repeats. We'll use our handy tool - an atom - to control when to stop the loop:</p><pre><code class="language-clojure">&#40;def working-hours?
  &quot;Am I working right now?&quot;
  &#40;atom false&#41;&#41;

&#40;defn let-people-through
  &quot;Send people into the PSK in batches as defined by
  `processing-batch-size` and `new-batch-in-mins`. Note that in our
  code we use seconds to represent minutes.&quot;
  &#91;active-applicants done-applicants&#93;
  &#40;future
    &#40;loop &#91;&#93;
      &#40;if @working-hours?
       &#40;do
         ;; let new people through
         ;; &lt;logic goes here&gt; ...
         ;; then sleep for a while
         &#40;Thread/sleep &#40;&#42; 1000 new-batch-in-mins&#41;&#41;
         ;; repeat
         &#40;recur&#41;&#41;

       &#40;ctl/info &quot;&#91;Entry&#93; Working hours are over! Closing Shop! Come back later!&quot;&#41;&#41;&#41;&#41;&#41;
</code></pre><p><a id="org8529d82"></a></p><h1 id="queuing_up_people_and_simulating_the_work_done_at_every_stage">Queuing up people and simulating the work done at every stage</h1><p>Now that people are coming into the PSK, we need a way to queue them up between stages. We also want to write a simulation for the work done at every counter. As described in the problem statement, the PSK is using simple FIFO queues between each stage. We will use the <code>LinkedBlockingQueue</code> data structure to represent these. This data structure is provided by the battle-tested <code>java.util.concurrent</code> package. Dropping down to Java when needed is a bonus Clojure superpower!</p><pre><code class="language-clojure">&#40;defn create-kendra-queues
  &quot;Given the counter-types / `stages` in the kendra, create the
  appropriate queues.&quot;
  &#91;kendra-stages q-capacity&#93;
  &#40;let &#91;queues-we-need &#40;-&gt; kendra-stages
                           keys
                           set
                           ;; Remove the stages where no queue of
                           ;; people is needed.
                           &#40;disj ::enter ::exit&#41;&#41;&#93;
    &#40;reduce &#40;fn &#91;m s&#93;
              &#40;assoc m
                     s &#40;LinkedBlockingQueue. q-capacity&#41;&#41;&#41;
            {}
            queues-we-need&#41;&#41;&#41;
</code></pre><p>Work at the counter involves the following:</p><ol><li>Pick the next person in the queue.</li><li>Call him to the counter by displaying his token number on the display.</li><li>Process the person, do the work.</li><li>Mark this stage as done. This will move him into the next queue.</li></ol><p>We can represent this in code as follows:</p><pre><code class="language-clojure">&#40;defn process-applicant
  &quot;Get a person from the queue. Process this person as per the rules
  of the counter.&quot;
  &#91;psk-agent my-queue notice-board&#93;
  &#40;if @working-hours?

    &#40;if-let &#91;person &#40;.poll my-queue 1 java.util.concurrent.TimeUnit/SECONDS&#41;&#93;
      &#40;let &#91;stage &#40;:type psk-agent&#41;
            stage-config &#40;:config psk-agent&#41;&#93;

        &#40;call-person-to-counter stage notice-board psk-agent person&#41;
        &#40;process-person stage stage-config psk-agent @person&#41;
        &#40;mark-processing-as-complete stage notice-board psk-agent person&#41;

        &#40;send-off &#42;agent&#42; process-applicant my-queue notice-board&#41;
        &#40;assoc psk-agent :last-processed &#40;person-representation @person&#41;&#41;&#41;

      &#40;do &#40;send-off &#42;agent&#42; process-applicant my-queue notice-board&#41;
          psk-agent&#41;&#41;

    &#40;ctl/info &#40;format &quot;&#91;Agent: %s&#93; Working hours are over! Closing Shop! Come back later!&quot;
                      &#40;agent-representation psk-agent&#41;&#41;&#41;&#41;&#41;
</code></pre><p>As we saw previously, we control the running of the code using the <code>working-hours?</code> atom. We're seeing something new here - the <code>send-off</code> function used with Clojure Agents. Ignore this for the time being, we'll come to an explanation of this after seeing refs and transactions.</p><p><a id="org0d27fed"></a></p><h1 id="keeping_track_of_people_and_the_display_board">Keeping track of people and the display board</h1><p>The tough part of this project is to keep track of the changes to each person's current state and the display board at every instant. These two views should always be consistent as multiple people are concurrently processed at different stages. Clojure makes this delightfully easy with refs and transactions.</p><h2 id="clojure_concurrency_primitive_-_refs_%28and_transactions%29">Clojure Concurrency Primitive - Refs (and transactions)</h2><p>Refs can be thought of as permanent pointers to mutable storage locations. The stored values can be safely changed - all together or none at all - using the functions <code>alter</code>, <code>ref-set</code> and <code>commute</code> within transactions. Clojure implements a Software Transactional Memory system<sup><a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink">1</a></sup> and gives us A,C, and I of the famous ACID properties<sup><a id="fnr.2" class="footref" href="#fn.2" role="doc-backlink">2</a></sup>. (Since it's in-mem there is no Durability). Using these transactions in code will be familiar to anyone with experience of using DB transactions.</p><p>In practice, updating values looks like this:</p><pre><code class="language-clojure">&#40;defn- call-person-to-counter
  &quot;Announce that person should come to the processing counter. Takes
  `person` and `notice-board` refs, performs a transactional update.&quot;
  &#91;stage notice-board psk-agent person&#93;
  &#40;dosync
   &#40;alter person
          assoc
          :stage stage
          :stage-status ::in-process
          :psk-agent &#40;agent-representation psk-agent&#41;&#41;
   &#40;store-stage-change person stage ::in-process&#41;
   &#40;alter notice-board
          assoc
          &#40;person-representation @person&#41;
          &#40;agent-representation psk-agent&#41;&#41;&#41;&#41;
</code></pre><p>Look Ma, no locks! This is much simpler, in my opinion, than thinking about which lock to take around which piece of data. Let's also check out the <code>store-state-change</code> function in the code above. This is a small data-collection function I wrote to calculate statistics about how much time each person takes in each stage.</p><pre><code class="language-clojure">&#40;defn- store-stage-change
  &quot;For the given `Person` ref, store the change to their stage for later analysis.&quot;
  &#40;&#91;person new-stage new-status&#93;
   &#40;store-stage-change person new-stage new-status &#40;ct/now&#41;&#41;&#41;
  &#40;&#91;person new-stage new-status time-instant&#93;
   &#40;let &#91;stage-log &#40;-&gt;StageHistory new-stage new-status time-instant&#41;&#93;
     &#40;dosync
      &#40;alter person
             update
             :stage-history
             conj
             stage-log&#41;&#41;&#41;&#41;&#41;
</code></pre><p>Writing this function is simple: we know we want to modify an existing person, so we wrap it in a transaction. The calling code happens to already be in a transaction, but Clojure will deal with this correctly and collapse all the work into a single transaction. From our point of view, we know that anytime this function is called, it is going to safely and permanently modify the person and store the stage-change in it.</p><p><a id="orgd9b0da0"></a></p><h1 id="processing_people_concurrently_across_all_open_counters">Processing people concurrently across all open counters</h1><p>The final piece of the puzzle is concurrently processing people on all the available counters. This is straightforward to do against a thread-pool, but Clojure provides another tool we can use: the <code>agent</code>.</p><h2 id="clojure_concurrency_primitive_-_agents">Clojure Concurrency Primitive - Agents</h2><p>Agents are another way to access/change mutable state, but they do this in an asynchronous manner. The functions <code>send</code> and <code>send-off</code> apply actions (functions) to the value held by the agent. The return value of the action becomes the new value of the agent. However, these actions execute in a different thread asynchronously. Clojure also guarantees execution in the order of submission. The value of the agent is inspectable at all times.</p><p>In our case, this allows us to represent processing counters as agents. The state of the agent is simply an identifier for it. Under the hood, each agent is spawning a thread from a thread-pool and executing a function. This function pulls the next person from the queue supplied to it, processes him, and sends another action to the current agent. We saw this function already, but let me post it again for clarity:</p><pre><code class="language-clojure">&#40;defn process-applicant
  &quot;Get a person from the queue. Process this person as per the rules
  of the counter.&quot;
  &#91;psk-agent my-queue notice-board&#93;
  &#40;if @working-hours?

    &#40;if-let &#91;person &#40;.poll my-queue 1 java.util.concurrent.TimeUnit/SECONDS&#41;&#93;
      &#40;let &#91;stage &#40;:type psk-agent&#41;
            stage-config &#40;:config psk-agent&#41;&#93;

        &#40;call-person-to-counter stage notice-board psk-agent person&#41;
        &#40;process-person stage stage-config psk-agent @person&#41;
        &#40;mark-processing-as-complete stage notice-board psk-agent person&#41;

        &#40;send-off &#42;agent&#42; process-applicant my-queue notice-board&#41;
        &#40;assoc psk-agent :last-processed &#40;person-representation @person&#41;&#41;&#41;

      &#40;do &#40;send-off &#42;agent&#42; process-applicant my-queue notice-board&#41;
          psk-agent&#41;&#41;

    &#40;ctl/info &#40;format &quot;&#91;Agent: %s&#93; Working hours are over! Closing Shop! Come back later!&quot;
                      &#40;agent-representation psk-agent&#41;&#41;&#41;&#41;&#41;
</code></pre><p>We create Agents as follows:</p><pre><code class="language-clojure">&#40;defrecord PSKAgent
    &#91;id type config&#93;&#41;

&#40;defn- create-agents
  &quot;For the given `agent-type`, create the given `num` of agents.&quot;
  &#91;agent-type stage-config num&#93;
  &#40;map &#40;comp agent &#40;fn &#91;i&#93; &#40;PSKAgent. &#40;inc i&#41; agent-type stage-config&#41;&#41;&#41;
       &#40;range num&#41;&#41;&#41;

&#40;defn create-kendra-agents
  &quot;Given the `stages` and their config for the kendra, create the
  appropriate agents to work these counters.&quot;
  &#91;kendra-stages&#93;
  &#40;let &#91;stages-with-counters &#40;-&gt; kendra-stages
                                 keys
                                 set
                                 ;; Remove the stages where no counter
                                 ;; of agents is needed.
                                 &#40;disj ::enter ::exit&#41;&#41;&#93;
    &#40;mapcat &#40;fn &#91;s&#93;
              &#40;let &#91;config &#40;get kendra-stages s&#41;&#93;
                &#40;create-agents s config &#40;:counters config&#41;&#41;&#41;&#41;
            stages-with-counters&#41;&#41;&#41;
</code></pre><p><a id="org02aab93"></a></p><h1 id="tying_everything_together_-_the_main_function">Tying everything together - the main function</h1><p>We tie all the pieces of the code together in our main function <code>start-the-kendra!</code>. The comments explain what each step is doing, for those of you unfamiliar with Clojure syntax.</p><pre><code class="language-clojure">&#40;defn start-the-kendra!
  &quot;Setup our Passport Seva Kendra.&quot;
  &#91;&#93;
  &#40;let &#91;;; Create queues for the various stages, returns a map of
        ;; stage-name -&gt; queue
        stage-&gt;queue &#40;create-kendra-queues stages total-capacity&#41;
        ;; Create all the agents
        list-of-agents &#40;create-kendra-agents stages&#41;
        ;; Create a display board for waiting members
        notice-board &#40;ref &#40;sorted-map&#41;&#41;
        ;; Track all the active applicants
        active-applicants &#40;ref &#91;&#93;&#41;
        ;; Track all the completed applicants &#40;for debugging /
        ;; historical data purpose&#41;
        done-applicants &#40;ref &#91;&#93;&#41;&#93;
    &#40;ctl/info &quot;&#91;PSK&#93; Welcome, today is a good day.&quot;&#41;
    ;; For each agent at each counter, start processing!
    &#40;doseq &#91;a list-of-agents&#93;
      ;; Get the stage this agent is working at, and the queue of
      ;; people for that stage.
      &#40;let &#91;s &#40;:type @a&#41;
            q &#40;stage-&gt;queue s&#41;&#93;
        ;; Start processing people from the queue concurrently in
        ;; independent threads.
        &#40;send-off a process-applicant q notice-board&#41;&#41;&#41;
    ;; Start a continuous future for applicants to periodically enter
    ;; the PSK.
    &#40;let-people-through active-applicants done-applicants&#41;
    ;; Start a helper process to move people from one stage to the
    ;; other.
    &#40;move-people-through stages stage-&gt;queue active-applicants&#41;
    ;; Return the data. We'll use this to monitor our system.
    &#91;notice-board active-applicants done-applicants&#93;&#41;&#41;
</code></pre><p>We haven't seen the <code>move-people-through</code> helper function used above yet. This is a simple <code>future</code> which regularly sweeps through all the people and moves a person done with one stage to the other.</p><p>Originally, I wrote the code such that each agent was aware of an input queue as well as an output queue. The agent understood that he had to pick the next person from the input queue and move that person to the output queue on completion. I refactored that out to show that it is simple to add functions around existing concurrent code which modify existing shared resources. The code for <code>move-people-through</code> looks like this:</p><pre><code class="language-clojure">&#40;defn move-people-through
  &quot;Review all the active applicants and move them into appropriate stages.&quot;
  &#91;kendra-stages stage-&gt;queue active-applicants&#93;
  &#40;future
    &#40;loop &#91;&#93;
      &#40;if @working-hours?

        &#40;let &#91;people @active-applicants&#93;
          &#40;doseq &#91;person &#40;-&gt;&gt; people
                              &#40;group-by &#40;comp :stage-status deref&#41;&#41;
                              ::done&#41;&#93;
            &#40;if &#40;= ::exit &#40;get-in kendra-stages &#91;&#40;:stage @person&#41; :next&#93;&#41;&#41;
              &#40;mark-applicant-process-as-complete person&#41;
              &#40;when-let &#91;next-stage &#40;get-in kendra-stages
                                            &#91;&#40;:stage @person&#41; :next&#93;&#41;&#93;
                &#40;move-applicant-to-next-stage stage-&gt;queue next-stage person&#41;&#41;&#41;&#41;

          &#40;Thread/sleep guide-people-to-next-stage-ms&#41;
          &#40;recur&#41;&#41;

        &#40;ctl/info &quot;&#91;Guide&#93; Working hours are over! Closing Shop! Come back later!&quot;&#41;&#41;&#41;&#41;&#41;
</code></pre><p>Both <code>mark-applicant-process-as-complete</code> and <code>move-applicant-to-next-stage</code> are tiny transactional updates to the person:</p><pre><code class="language-clojure">&#40;defn- mark-applicant-process-as-complete
  &quot;Takes a `person` ref object and marks its processing as complete.&quot;
  &#91;person&#93;
  &#40;let &#91;time-instant &#40;ct/now&#41;&#93;
    &#40;dosync
     &#40;alter person
            assoc
            :stage ::exit
            :exit-time time-instant
            :total-time &#40;-&gt; @person
                            :enter-time
                            &#40;ct/interval time-instant&#41;
                            ct/in-seconds&#41;&#41;
     &#40;store-stage-change person ::exit ::done time-instant&#41;&#41;&#41;&#41;

&#40;defn- move-applicant-to-next-stage
  &quot;Given a `person` ref and the next stage they should go to, move
  them to the stage. Does a transactional update.&quot;
  &#91;stage-&gt;queue next-stage person&#93;
  &#40;dosync
   &#40;alter person
          assoc
          :stage next-stage
          :stage-status ::waiting&#41;
   &#40;store-stage-change person next-stage ::waiting&#41;&#41;
  &#40;.put &#40;stage-&gt;queue next-stage&#41; person&#41;&#41;
</code></pre><p>I also added a book-keeping function when letting people into the PSK. This function removes completed applicants from the list of active applicants. This frees up PSK capacity. I also move this data to a different list, because it's fun to go through it and look for interesting insights.</p><pre><code class="language-clojure">&#40;defn- book-keeping-for-applicants
  &quot;Remove all applicants who are completely done from
  `active-applicants`. Store them in `done-applicants` for
  book-keeping.

  &#42;NOTE&#42; : Since this goes through the entire collection, it is slow.
  Hence we run it when sending in new batches of people.&quot;
  &#91;active-applicants done-applicants&#93;
  &#40;dosync
   &#40;let &#91;&#91;active-people done-people&#93; &#40;reduce &#40;fn &#91;&#91;aa da&#93; p&#93;
                                               &#40;if &#40;and &#40;= &#40;:stage @p&#41; ::exit&#41;
                                                        &#40;= &#40;:stage-status @p&#41; ::done&#41;&#41;
                                                 &#91;aa &#40;conj da p&#41;&#93;
                                                 &#91;&#40;conj aa p&#41; da&#93;&#41;&#41;
                                             &#91;&#91;&#93; &#91;&#93;&#93;
                                             @active-applicants&#41;&#93;
     &#40;ref-set active-applicants active-people&#41;
     &#40;alter done-applicants into done-people&#41;&#41;&#41;&#41;
</code></pre><p>Here we use the <code>ref-set</code> and <code>alter</code> functions to reset the value of active-applicants and modify the value of the done-applicants.</p><p><a id="orgedd2c48"></a></p><h1 id="with_me_so_far%3F_some_thoughts">With me so far? Some thoughts</h1><ul><li>Clojure's concurrency primitives make it simple for me to <strong>think</strong> about this problem. I wrote the code like I would write a high-level pseudo description of the problem, and it worked just fine. I think this simplification is a huge benefit when dealing with concurrent code.</li><li>The ability to write and test small bits of concurrent code is a big win. It was simple for me to modify the original code and devise experiments around it.</li><li>We haven't really looked at what this looks like when it's running! Let's do that now!</li></ul><p><a id="org4a50143"></a></p><h1 id="can_we_see_the_problem%3F">Can we see the problem?</h1><p>Let's run this system! We're setting up a small loop to display the board. We're running much fewer counters than the actual PSK. This is in order to make the display board consumable.</p><p><img src="/assets/images/psk-run-1.gif" alt="img" title="A normal day at the Passport Seva Kendra" /></p><p>Things look good in this loop, the board looks predictable. The problem occurs when someone gets unlucky at one stage, and spends much more time there than the average person. Let's repeat the run by marking someone as unlucky, and by increasing the number of counters processing earlier stages (which matches with reality)</p><pre><code class="language-clojure">&#40;def unlucky-applicant?
  &quot;Introduce a little anarchy!&quot;
  &#40;atom #{&quot;N-3&quot; &quot;S-2&quot;}&#41;&#41;

&#40;defn- process-person
  &quot;Do the work for processing the given person. Takes a `person`
  object and not a ref.&quot;
  &#91;stage stage-config psk-agent person&#93;
  &#40;let &#91;processing-time&#42; &#40;get-processing-time-for-stage stage-config&#41;
        processing-time &#40;if &#40;@unlucky-applicant? &#40;person-representation person&#41;&#41;
                          ;; You will need more time because the gods
                          ;; are against you.
                          &#40;&#42; 10 processing-time&#42;&#41;
                          processing-time&#42;&#41;&#93;
    &#40;Thread/sleep processing-time&#41;&#41;&#41;
</code></pre><p>Here is what this looks like:</p><p><img src="/assets/images/psk-run-2.gif" alt="img" title="N-3 is not having a good day." /></p><p>As you can see, there are a number of people ahead of N-3 by the time he's done with stage 0. N-40s are being processed in stage 0 at this point in time. He has no idea where he is in the queue of people, and must keep staring at the display board at all times.</p><p>I will speak briefly about the three solutions that we initially proposed.</p><h2 id="solution_1%3A_use_priority_queues">Solution 1: Use priority queues</h2><p>Using priority queues between each stage solves the problem of the applicant not knowing when he is next. However, there are two points to think about:</p><ol><li>The priority queue solves the problem within a given category, but not across categories. You know that you are the next N- category person at counter B, but you don't know when that will be because of all the S/T/P category people that will be served first.</li><li>A potential drawback of this may be: if a certain set of applicants are always slow at each stage, then in this model they will bring down the average number of people who will be served by the PSK (since we will prioritize serving them over people who have moved ahead of them).</li></ol><p>Making the change to use priority queues in our code is trivial. We go back to <code>java.util.concurrent</code> and swap out our <code>LinkedBlockingQueue</code> in favor of a <code>PriorityBlockingQueue</code>. Now all we have to do is provide a comparator function. The code for this is <a href='https://gist.github.com/vedang/969a726e1f49f5fc550268a22c4e4b0d#file-psk-clj-L393-L420'>here</a> We can also generate timing samples across people through repeated runs of the program. This will give us an idea of whether the average processing time is affected by using a Priority Queue or not.</p><h2 id="solution_2%3A_new_token_numbers_per_stage">Solution 2: New token numbers per stage</h2><p>Using new token numbers solves the problem elegantly. This is probably not used because of the logistical difficulty of handing out new tokens to applicants again and again. In the real world, I'm sure that this process may cause confusion if not carefully designed. In our system, we already have a perfectly good way of getting the next token number - our atomic token generator. Implementing this solution is straight-forward and left as an exercise for the reader! (This blog post is already quite lengthy!)</p><h2 id="solution_3%3A_%22where_am_i%3F%22_estimated_wait_time">Solution 3: "Where am I?" Estimated Wait Time</h2><p>Another way the PSK can help the applicants is by providing a separate self-serve display. The applicant enters his token number and gets to see how many people are ahead of him for the given stage. Both <code>LinkedBlockingQueue</code> as well as <code>PriorityBlockingQueue</code> provide a <code>toArray</code> function which returns all the elements of the queue in order.</p><p>As we are tracking the time each person takes at each stage, we can also predict the estimated amount of time this person would have to wait. This could be an entire blog-post in itself<sup><a id="fnr.3" class="footref" href="#fn.3" role="doc-backlink">3</a></sup>.</p><p>I leave this as an exercise for the reader :)</p><p><a id="org96cb052"></a></p><h1 id="final_thoughts">Final Thoughts</h1><p>The complete code for this exercise can be found <a href='https://gist.github.com/vedang/969a726e1f49f5fc550268a22c4e4b0d'>here</a>. The <a href='https://gist.github.com/vedang/969a726e1f49f5fc550268a22c4e4b0d#file-psk-clj-L530-L545'>comment block</a> at the end of the gist explains how to run the program against a Clojure REPL. Note that this is not a trivial simulation of the PSK, I believe that the entire code can be actually used by them with a small set of changes. A list of things that we have not implemented:</p><ol><li>In the real world, we'd need to store each person's information in a DB along the way. Since the in-mem structures are guaranteed to be consistent, this is something we can achieve by periodically reading information about all applicants and committing it to the DB (similar to how <code>book-keeping-for-applicants</code> works).</li><li>We haven't implemented the error and corrections flow. Failure to pass a stage can be represented as another state (say <code>::error</code>). The <code>process-applicant</code> code will identify success/failure of the stage and set the appropriate state. <code>move-people-through</code> would need minor changes to deal with this. I leave this as an exercise for the reader.</li><li>In the real world, we'd need to build inputs for a real PSK employee to inform us that processing is done. This is nothing but a loop inside <code>process-applicant</code> which checks the DB to see if the work is done.</li></ol><p>I'd love to hear feedback about this post. Is there a better way to implement this? Tell me. Am I missing use-cases of the PSK and implementing a solution to a much simpler problem? Do tell! There may be drawbacks to solutions I've proposed that I cannot see, and there may be valid reasons the system is built the way it is. I'd love to understand the real-world problems that I've missed.</p><p>If you're interested in working with Clojure, on problems like Estimated Wait Time, <a href='https://jobs.lever.co/helpshift/'>we're hiring</a>! Send us an email at <a href='mailto:jobs@helpshift.com'>jobs@helpshift.com</a>.</p><p><em>A big thank you to the following people for reviewing initial drafts of this post: Kapil Reddy, Kiran Kulkarni, Mourjo Sen, Suvrat Apte, Dinesh Chhatani.</em></p><p><em>A big thank you to <a href='https://twitter.com/renderkid'>Bhargava Chowdary</a> for creating the illustration of the PSK.</em></p><p><a id="org3f50c59"></a></p><h1 id="references">References</h1><ul><li>Eric Normand's post explaining all Clojure Concurrency primitives: <a href="https://purelyfunctional.tv/guide/clojure-concurrency/">https://purelyfunctional.tv/guide/clojure-concurrency/</a></li><li>Rich Hickey's talk on Clojure Concurrency: <a href="https://www.youtube.com/watch?v=nDAfZK8m5_8">https://www.youtube.com/watch?v=nDAfZK8m5_8</a></li><li>Atoms: <a href="https://clojure.org/reference/atoms">https://clojure.org/reference/atoms</a></li><li>Refs: <a href="https://clojure.org/reference/refs">https://clojure.org/reference/refs</a></li><li>Futures: <a href="https://clojuredocs.org/clojure.core/future">https://clojuredocs.org/clojure.core/future</a></li><li>Agents: <a href="https://clojure.org/reference/agents">https://clojure.org/reference/agents</a></li></ul><h2 id="footnotes">Footnotes</h2><p><sup><a id="fn.1" class="footnum" href="#fnr.1">1</a></sup> STM: <a href="http://en.wikipedia.org/wiki/Software_transactional_memory">http://en.wikipedia.org/wiki/Software_transactional_memory</a></p><p><sup><a id="fn.2" class="footnum" href="#fnr.2">2</a></sup> ACID: <a href="https://en.wikipedia.org/wiki/ACID_(computer_science)">https://en.wikipedia.org/wiki/ACID_(computer_science)</a></p><p><sup><a id="fn.3" class="footnum" href="#fnr.3">3</a></sup> Estimating wait-times: <a href="https://en.wikipedia.org/wiki/Queueing_theory">https://en.wikipedia.org/wiki/Queueing_theory</a></p>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/doug-mcilroy-unix-philosophy/</id>
    <link href="https://vedang.me/doug-mcilroy-unix-philosophy/"/>
    <title>Doug McIlroy: the UNIX philosophy</title>
    <updated>2018-06-12T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><em>Snippet from: <a href='https://homepage.cs.uri.edu/~thenry/resources/unix_art/ch01s06.html'>The Art of Unix Programming</a></em></p><ul><li><strong>Make each program do one thing well</strong>. To do a new job, build afresh rather than complicate old programs by adding new features.</li><li><strong>Expect the output of every program to become the input to another</strong>, as yet unknown, program. Don't clutter output with extraneous information. Avoid stringently columnar or binary input formats. Don't insist on interactive input.</li><li><strong>Design and build software, even operating systems, to be tried early, ideally within weeks</strong>. Don't hesitate to throw away the clumsy parts and rebuild them.</li><li><strong>Use tools in preference to unskilled help to lighten a programming task</strong>, even if you have to detour to build the tools and expect to throw some of them out after you've finished using them.</li></ul>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/rob-pike-rules/</id>
    <link href="https://vedang.me/rob-pike-rules/"/>
    <title>Rob Pike: Rules of Programming</title>
    <updated>2018-06-12T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><em>Snippet from: <a href='https://www.lysator.liu.se/c/pikestyle.html'>Notes on C Programming</a></em></p><ul><li><strong>No Speed Hacks</strong> : You can't tell where a program is going to spend its time. Bottlenecks occur in surprising places, so don't try to second guess and put in a speed hack until you've proven that's where the bottleneck is.</li><li><strong>Measure before tuning</strong> : Don't tune for speed until you've measured, and even then don't unless one part of the code overwhelms the rest.</li><li><strong>No Fancy Algorithms</strong> : Fancy algorithms are slow when n is small, and n is usually small. Fancy algorithms have big constants. Until you know that n is frequently going to be big, don't get fancy. (Even if n does get big, use Rule 2 first.)</li><li><strong>Use Simple Data Structures</strong> : Fancy algorithms are buggier than simple ones, and they're much harder to implement. Use simple algorithms as well as simple data structures.</li><li><strong>Data Dominates</strong> : If you've chosen the right data structures and organized things well, the algorithms will almost always be self-evident. Data structures, not algorithms, are central to programming.</li></ul>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/esr-unix-philosophy/</id>
    <link href="https://vedang.me/esr-unix-philosophy/"/>
    <title>ESR: the UNIX Philosophy</title>
    <updated>2018-06-12T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><em>Snippet from: <a href='https://homepage.cs.uri.edu/~thenry/resources/unix_art/ch01s06.html'>The Art of Unix Programming</a></em></p><ul><li><strong>Rule of Modularity</strong> : Write simple parts connected by clean interfaces.</li><li><strong>Rule of Clarity</strong> : Clarity is better than cleverness.</li><li><strong>Rule of Composition</strong> : Design programs to be connected to other programs.</li><li><strong>Rule of Separation</strong> : Separate policy from mechanism; separate interfaces from engines.</li><li><strong>Rule of Simplicity</strong> : Design for simplicity; add complexity only where you must.</li><li><strong>Rule of Parsimony</strong> : Write a big program only when it is clear by demonstration that nothing else will do.</li><li><strong>Rule of Transparency</strong> : Design for visibility to make inspection and debugging easier.</li><li><strong>Rule of Robustness</strong> : Robustness is the child of transparency and simplicity.</li><li><strong>Rule of Representation</strong> : Fold knowledge into data so program logic can be stupid and robust.</li><li><strong>Rule of Least Surprise</strong> : In interface design, always do the least surprising thing.</li><li><strong>Rule of Silence</strong> : When a program has nothing surprising to say, it should say nothing.</li><li><strong>Rule of Repair</strong> : When you must fail, fail noisily and as soon as possible.</li><li><strong>Rule of Economy</strong> : Programmer time is expensive; conserve it in preference to machine time.</li><li><strong>Rule of Generation</strong> : Avoid hand-hacking; write programs to write programs when you can.</li><li><strong>Rule of Optimization</strong> : Prototype before polishing. Get it working before you optimize it.</li><li><strong>Rule of Diversity</strong> : Distrust all claims for the "one true way".</li><li><strong>Rule of Extensibility</strong> : Design for the future, because it will be here sooner than you think.</li></ul>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/mbsync-channel-is-locked/</id>
    <link href="https://vedang.me/mbsync-channel-is-locked/"/>
    <title>Mbsync: Error: Channel &lt;x&gt; Is Locked</title>
    <updated>2017-10-11T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>You may come across the following error when running <code>mbsync</code> :</p><pre><code>Error: channel :&lt;channel-name&gt;-remote:&lt;folder&gt;-:&lt;channel-name&gt;-local:&lt;folder&gt; is locked
</code></pre><p>This happens when there is another <code>mbsync</code> instance running in parallel and it has taken a lock on the particular folder. In my case, I am running <code>mbsync</code> via a cron-job, and it tends to leave dead instances around if the laptop sleeps during a run. Kill all instances of <code>mbsync</code> and restart the process and you should be fine!</p>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/imagine-working-in-engineering-at-helpshift/</id>
    <link href="https://vedang.me/imagine-working-in-engineering-at-helpshift/"/>
    <title>Imagine Working in Engineering at Helpshift</title>
    <updated>2017-09-07T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><div class="orgtoc"></p><ul><li><a href='#org4716c40'>What does your day-to-day at Helpshift look like?</a></li><li><a href='#org37a7b79'>What has been your favorite off-site team oriented event, and why?</a></li><li><a href='#orgadfbee9'>What role(s) did you start in at Helpshift and how have you graduated/migrated into different roles?</a></li><li><a href='#orge5f8364'>Favourite thing about working for an SF tech startup:</a></li><li><a href='#org3699c28'>What advice would you have for prospective Helpshift candidates?</a></li><li><a href='#org5df84e4'>What is your proudest moment at Helpshift?</a></li><li><a href='#orgfc1f1f2'>How would you describe the company culture and what do you like most about it?</a></li><li><a href='#org6ef9d7d'>Superpower (real or fake): Obscure Emacs shortcuts.</a></li><li><a href='#org73be9b8'>Slightly addicted to: Tea.</a></li><li><a href='#org13212bb'>Always on the search for the perfect: new book to read.</a></li><li><a href='#orgd6ec17e'>If you could interview one person, dead or alive, who would it be and why?</a></div></li></ul><p><em>Cross-posted from my interview published on <a href='https://medium.com/@helpshift/image-ine-working-in-engineering-at-helpshift-8818247e2188'>Helpshift's Medium blog</a></em></p><ul><li>Name: Vedang Manerikar</li><li>Current Role: Principal Engineering Manager, Backend</li><li>How long you have been with Helpshift?: 7 years</li></ul><p><a id="org4716c40"></a></p><h1 id="what_does_your_day-to-day_at_helpshift_look_like%3F">What does your day-to-day at Helpshift look like?</h1><p>My day-to-day is unblocking people and getting things done!</p><p>I review architecture, read code, enable customer service, help product guys understand the engineering pros and cons of their product decisions and prioritize work for maximum productivity. I work with an amazing team. They make all of this possible and achievable.</p><p>That means I also get to hang out with my best friends, catch up on their day-to-day, laugh at inside jokes that have been running for years and overall have a blast. Seven years at a place lets you build deep relationships and puts a smile on your face when you think about work!</p><p><a id="org37a7b79"></a></p><h1 id="what_has_been_your_favorite_off-site_team_oriented_event%2C_and_why%3F">What has been your favorite off-site team oriented event, and why?</h1><p>When we were cash-strapped and raising funds, a team off-site was the running joke of the company. "After Series A!", we would raise our glasses and say. As time went on without funding in sight, every request turned into "After Series A!".</p><p>So when we did raise our first round, we treated ourselves to a long weekend at a spectacular villa in Goa. It was a heady celebration and the world was our oyster. It remains my favorite off-site because the funding felt like validation.</p><p><a id="orgadfbee9"></a></p><h1 id="what_role%28s%29_did_you_start_in_at_helpshift_and_how_have_you_graduated%2Fmigrated_into_different_roles%3F">What role(s) did you start in at Helpshift and how have you graduated/migrated into different roles?</h1><p>I came to Helpshift with a strong systems background: writing kernel modules and working on storage replication. I started writing Python at Helpshift and then graduated to Clojure (in 2011, when almost no one else was writing Clojure). I've since written and helped design large chunks of the Helpshift architecture, and have learnt interesting things about distributed systems through trial-by-fire. Today, I make an impact by making sure my team has clear, unambiguous goals and all the resources to achieve these goals.</p><p><a id="orge5f8364"></a></p><h1 id="favourite_thing_about_working_for_an_sf_tech_startup%3A">Favourite thing about working for an SF tech startup:</h1><p>I have a lot of "favorite things" about working at Helpshift (We keep our workplace fun!). A small example of this is that on any given day someone or the other will bring home-made snacks and goodies for the others to enjoy.</p><p><a id="org3699c28"></a></p><h1 id="what_advice_would_you_have_for_prospective_helpshift_candidates%3F">What advice would you have for prospective Helpshift candidates?</h1><p>We're still a small enough company that everyone knows everyone else by name. If you're the kind of person who wants to get in on the ground floor and grow with a company, we're perfect for you. Come with a no-nonsense, no-bullshit attitude. Be a kind person.</p><p><a id="org5df84e4"></a></p><h1 id="what_is_your_proudest_moment_at_helpshift%3F">What is your proudest moment at Helpshift?</h1><p>My colleagues and I have taken this company from serving 0 requests per second to serving 50,000 requests per second. We're installed in over 2 Billion devices in the world. That means that there's a 50% chance that you (reader) have us installed in one of the apps on your phone. That sentence is a source of great pride for me.</p><p><a id="orgfc1f1f2"></a></p><h1 id="how_would_you_describe_the_company_culture_and_what_do_you_like_most_about_it%3F">How would you describe the company culture and what do you like most about it?</h1><p>As a company, we've always looked for attitude over smarts. We want people who are good listeners and good communicators. We want people who are passionate and hold strong opinions. We want people who care about quality. This is the culture that we've always striven to maintain at Helpshift.</p><p><a id="org6ef9d7d"></a></p><h1 id="superpower_%28real_or_fake%29%3A_obscure_emacs_shortcuts.">Superpower (real or fake): Obscure Emacs shortcuts.</h1><p>Give a man Emacs and he'll get some programming done. Teach a man how to tweak it and play with it and he'll happily shave yaks for the rest of his life.</p><p><a id="org73be9b8"></a></p><h1 id="slightly_addicted_to%3A_tea.">Slightly addicted to: Tea.</h1><p>Walking out of the office to have road-side tea in the evening is an old Helpshift tradition. I'm always on the lookout for great Masala Chai, and if you come to my home I'll whip up a fantastic cup of tea for us to chat over!</p><p><a id="org13212bb"></a></p><h1 id="always_on_the_search_for_the_perfect%3A_new_book_to_read.">Always on the search for the perfect: new book to read.</h1><p>Reading is one of my favorite ways to unwind. I read anything that I can get my hands on: fantasies, travelogues, thrillers, pop psychology, autobiographies, technical books, comics, you name it!</p><p><a id="orgd6ec17e"></a></p><h1 id="if_you_could_interview_one_person%2C_dead_or_alive%2C_who_would_it_be_and_why%3F">If you could interview one person, dead or alive, who would it be and why?</h1><p>Isaac Newton or Albert Einstein or Alan Turing! (Sorry I cheated.) These were phenomenal people with world-changing ideas. How did they get them? How did they remain productive all their life? What did "work" mean for them? What was their work day like? What did they do to unwind? What did they do for leisure? Who did <span class="underline">they</span> look up to? I'd love to know these answers!</p>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/what-your-programming-editor-should-do/</id>
    <link href="https://vedang.me/what-your-programming-editor-should-do/"/>
    <title>What Your Programming Editor Should Be Able To Do.</title>
    <updated>2016-11-11T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><em>As a programmer, you spend 80% of your time in the editor. You need to master it if you want to boost your productivity. The artisan who doesn't care for their tools is no artisan at all.</em></p><p><em>This is an incomplete list of the things your editor should be able to do for you. Please help me make it better by suggesting functionality that you find indispensable in your editor.</em></p><p><em>I have tried to arrange it in decreasing order of importance. I consider all this functionality table stakes, but if you must prioritise then start from the top.</em></p><p><div class="orgtoc"></p><ul><li><a href='#h:C1BC272B-3DD1-4B28-A41D-19E109A1F25B'>Complete Anything (In Emacs: company, capf, eldoc)</a></li><li><a href='#h:6D5CFE7F-B343-4258-A192-66C4A9AC1246'>Jump to source and back (In Emacs: xref, via dumb-jump or eglot)</a></li><li><a href='#h:F5800922-1D27-4114-B92B-D37E7F5C07E3'>Check your syntax as you type (In Emacs: flycheck, flymake)</a></li><li><a href='#h:41288E34-597C-43A3-AFFD-9CF9FF1C27BF'>Understand and manipulate your project structure (In Emacs: project.el)</a></li><li><a href='#h:3CFF166B-8276-4893-95E3-604A909A3604'>Use Git from the editor (In Emacs: magit)</a></li><li><a href='#h:5D828AB4-F397-49A5-9F9E-2645E288CB5E'>Edit code as a structured entity (In Emacs: paredit)</a></li><li><a href='#h:3FE6B133-CAFC-488E-A269-EC3615A69000'>Jump to important points in your code quickly (In Emacs: imenu and avy, helm)</a></li><li><a href='#h:B6F236D5-9B9E-46CC-83D9-D0B09DF8CF4A'>Understand and refactor your programming language (In Emacs: eglot, cider, clj-refactor, clojure-lsp)</a></li><li><a href='#h:D3394149-5B4A-42E8-9973-7EE3D2B16C68'>Run your tests on-demand, and show you the results (In Emacs: cider-test)</a></li><li><a href='#h:2D856AE1-9B6D-4CD3-9C84-DE8E19A7618E'>Debug your code using a powerful debugger (In Emacs: gud, cider-debug, dap-mode)</a></li><li><a href='#h:1B7958C9-9E7B-4300-9B2D-3BADAD132D02'>Support templates and expansion (In Emacs: yasnippet, org-capture)</a></li><li><a href='#h:044126B9-4B1A-4B0A-ABF0-3527129A957E'>Support automating fast edits (In Emacs: kmacro-start-macro, multiple-cursors)</a></li><li><a href='#h:5E53DFB2-370E-4EC3-B5E3-C167716BCD6D'>Upcoming Experimental features</a></div></li></ul><p><a id="h:C1BC272B-3DD1-4B28-A41D-19E109A1F25B"></a></p><h1 id="complete_anything_%28in_emacs%3A_company%2C_capf%2C_eldoc%29">Complete Anything (In Emacs: company, capf, eldoc)</h1><p>Your editor should be able to show you completions for anything you are currently typing. This list includes, but is not restricted to:</p><ul><li>Completion of functions, variables, types, namespaces, classes, plain-text.</li><li>Completion of arguments, with doc-strings</li></ul><p><em>Note: <code>eldoc</code> is an Emacs built-in</em></p><p><hr></p><p><a id="h:6D5CFE7F-B343-4258-A192-66C4A9AC1246"></a></p><h1 id="jump_to_source_and_back_%28in_emacs%3A_xref%2C_via_dumb-jump_or_eglot%29">Jump to source and back (In Emacs: xref, via dumb-jump or eglot)</h1><p>Your editor should be able to jump to the definition of something on demand. This can be symbol at point (meaning word under the cursor) or can be anything I choose to enter.</p><ul><li><strong>M-.</strong> : Jump to the source of something</li><li><strong>M-,</strong> : Jump back from the source of something</li></ul><p><em>Note: <code>xref</code> is the built-in Emacs jump mechanism, <code>eglot</code> is the built-in Emacs LSP client</em></p><p><hr></p><p><a id="h:F5800922-1D27-4114-B92B-D37E7F5C07E3"></a></p><h1 id="check_your_syntax_as_you_type_%28in_emacs%3A_flycheck%2C_flymake%29">Check your syntax as you type (In Emacs: flycheck, flymake)</h1><p>Your editor must be able to integrate with a linter program. As you are typing code, it should be able to point out all the problems in your code without getting in your way or slowing you down.</p><p><em>Note: <code>flymake</code> is the built-in Emacs mechanism for integrating checkers and linters</em></p><p><code>flycheck</code> key-bindings:</p><ul><li><strong>C-c ! l</strong> : List all errors</li><li><strong>C-c ! n</strong> : Go to the next error</li><li><strong>C-c ! p</strong> : Go to the previous error</li><li><strong>M-g M-n</strong> : Go to the next error</li><li><strong>M-g M-p</strong> : Go to the previous error</li></ul><p><hr></p><p><a id="h:41288E34-597C-43A3-AFFD-9CF9FF1C27BF"></a></p><h1 id="understand_and_manipulate_your_project_structure_%28in_emacs%3A_project.el%29">Understand and manipulate your project structure (In Emacs: project.el)</h1><p>Your editor should have an understanding of the current project you are working in, as a whole. You should be able to quickly search for occurrences of certain words / phrases / symbols across the project. You should be able to pull up any file you wish in the project quickly, without needing to know exactly how the project is structured on disk</p><p><em>Note: <code>project.el</code> is the built-in Emacs project manager</em></p><p><code>project.el</code> key-bindings:</p><ul><li><strong>C-x p p</strong> : Switch to a project on disk</li><li><strong>C-x p f</strong> : Find a file in the current project</li><li><strong>C-x p G</strong> : Search for something in the current project</li></ul><p><hr></p><p><a id="h:3CFF166B-8276-4893-95E3-604A909A3604"></a></p><h1 id="use_git_from_the_editor_%28in_emacs%3A_magit%29">Use Git from the editor (In Emacs: magit)</h1><p>Your editor should be able to give you the full power of your version control system in your editor. This includes, but is not limited to: creating new commits, listing changes made in a certain file(s), jumping to different versions of the code in the VCS.</p><p><code>magit</code> key-bindings:</p><ul><li><strong>M-x magit-status</strong> : Access the magit-status buffer for your project.</li><li><strong>C-c C-c</strong> : Access the full set of possible actions in your buffer</li><li><strong>M-x magit-blame-addition</strong> : For each line of the current buffer, show the revision which it was added in</li></ul><p><hr></p><p><a id="h:5D828AB4-F397-49A5-9F9E-2645E288CB5E"></a></p><h1 id="edit_code_as_a_structured_entity_%28in_emacs%3A_paredit%29">Edit code as a structured entity (In Emacs: paredit)</h1><p>Your editor should be able to understand the semantics of your programming language. It should be able to recognise and move quickly between functions and expressions. It should be able to manipulate entire expressions in one go.</p><ul><li><strong>C-a</strong> : <code>beginning-of-line</code></li><li><strong>C-e</strong> : <code>end-of-line</code></li><li><strong>M-a</strong> : <code>backward-sentence</code></li><li><strong>M-e</strong> : <code>forward-sentence</code></li><li><strong>C-M-f</strong> : <code>forward-sexp</code></li><li><strong>C-M-b</strong> : <code>backward-sexp</code></li><li><strong>C-M-a</strong> : <code>beginning-of-defun</code></li><li><strong>C-M-e</strong> : <code>end-of-defun</code></li><li><strong>M-m</strong> : <code>back-to-indentation</code></li><li><strong>C-)</strong> : <code>paredit-forward-slurp-sexp</code></li><li><strong>C-(</strong> : <code>paredit-backward-slurp-sexp</code></li><li><strong>C-}</strong> : <code>paredit-forward-barf-sexp</code></li><li><strong>C-{</strong> : <code>paredit-backward-barf-sexp</code></li></ul><p><hr></p><p><a id="h:3FE6B133-CAFC-488E-A269-EC3615A69000"></a></p><h1 id="jump_to_important_points_in_your_code_quickly_%28in_emacs%3A_imenu_and_avy%2C_helm%29">Jump to important points in your code quickly (In Emacs: imenu and avy, helm)</h1><p>Your editor should be able to jump to all the important points of your code in the least number of keystrokes (i.e. quickly). You should be able to quickly see all function names, type declarations, variable declarations in a given file and ideally in a given project as well. You should be able to jump to any word on the screen quickly.</p><p><em>Note: <code>imenu</code> is a built-in Emacs feature</em>.</p><p>My <code>helm</code> and <code>avy</code> key-bindings:</p><ul><li><strong>C-x c i</strong> : Show all the function, type, variable declaration names and be able to jump to any of them with a few keystrokes</li><li><strong>C-x c b, C-c n</strong> : Cycle through all the "sessions" and resume any you wish. A session here is a search of any kind that you have done before: for a file name, symbol name, variable listing, bookmarks etc</li><li><strong>M-g SPC</strong> : Jump to a word on screen by typing the starting character</li></ul><p><hr></p><p><a id="h:B6F236D5-9B9E-46CC-83D9-D0B09DF8CF4A"></a></p><h1 id="understand_and_refactor_your_programming_language_%28in_emacs%3A_eglot%2C_cider%2C_clj-refactor%2C_clojure-lsp%29">Understand and refactor your programming language (In Emacs: eglot, cider, clj-refactor, clojure-lsp)</h1><p>Your editor should have a clear understanding of the code you are writing, and should be able to help you change it faster. This includes, but is not limited to:</p><p><em>Note: <code>eglot</code> is the built-in LSP client for Emacs. <code>cider</code>, <code>clj-refactor</code> and <code>clojure-lsp</code> are Emacs packages specific to the Clojure programming language.</em></p><ul><li>Refactoring support:<ul><li>Being able to rename symbols across the project</li><li>Being able to suggest code clean up</li><li>Being able to quickly import / clean-up dependencies</li></ul></li><li>Finding usages of code:<ul><li>Being able to find all the places where a certain function is being used</li></ul></li></ul><p><hr></p><p><a id="h:D3394149-5B4A-42E8-9973-7EE3D2B16C68"></a></p><h1 id="run_your_tests_on-demand%2C_and_show_you_the_results_%28in_emacs%3A_cider-test%29">Run your tests on-demand, and show you the results (In Emacs: cider-test)</h1><p>Your editor should be able to run the tests of your project, integrating with your test-runner. It should be able to run these tests on-demand, and show the results of running the tests. You should be able to re-run all the tests, or only the failed tests. This depends on the test-runner and not the editor, so check whether your test-runner is powerful enough.</p><p><code>cider-test</code> is provided by the <code>cider</code> Emacs package for running Clojure project tests. Other languages have similar modes provided for their projects.</p><p><hr></p><p><a id="h:2D856AE1-9B6D-4CD3-9C84-DE8E19A7618E"></a></p><h1 id="debug_your_code_using_a_powerful_debugger_%28in_emacs%3A_gud%2C_cider-debug%2C_dap-mode%29">Debug your code using a powerful debugger (In Emacs: gud, cider-debug, dap-mode)</h1><p>Your editor should integrate with and start a debugger on demand. At least the basics of debugging (set breakpoints, step-in, step-out, see local variables) should be supported.</p><p><em>Note: In Emacs, <code>gud</code> is built-in and integrates with a variety of debuggers.</em></p><p><hr></p><p><a id="h:1B7958C9-9E7B-4300-9B2D-3BADAD132D02"></a></p><h1 id="support_templates_and_expansion_%28in_emacs%3A_yasnippet%2C_org-capture%29">Support templates and expansion (In Emacs: yasnippet, org-capture)</h1><p>Your editor should support creation and fast usage of templates (snippets) for common boiler-plate functionality. This speeds up development and documentation tremendously.</p><p><hr></p><p><a id="h:044126B9-4B1A-4B0A-ABF0-3527129A957E"></a></p><h1 id="support_automating_fast_edits_%28in_emacs%3A_kmacro-start-macro%2C_multiple-cursors%29">Support automating fast edits (In Emacs: kmacro-start-macro, multiple-cursors)</h1><p>Your editor should be able to help you with one-off weird edits, eg: automatically numbering every new line with an incrementing counter.</p><p><hr></p><p><a id="h:5E53DFB2-370E-4EC3-B5E3-C167716BCD6D"></a></p><h1 id="upcoming_experimental_features">Upcoming Experimental features</h1><p>I am currently working on fine-tuning my editor experience to work best with LLMs and Code Assistants. The current state-of-the-art for this is the VSCode fork <a href='https://www.cursor.com/'>Cursor</a>, and there is <a href='https://sourcegraph.com/blog/the-self-driving-ide-is-coming'>excellent</a> <a href='https://docs.github.com/en/copilot/about-github-copilot'>work</a> <a href='https://aider.chat/'>happening</a> in this area.</p><p>Stay tuned!</p><p><hr></p>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/draupadi-and-the-pandavas/</id>
    <link href="https://vedang.me/draupadi-and-the-pandavas/"/>
    <title>Draupadi and the Pandavas</title>
    <updated>2012-09-21T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><div class="orgtoc"></p><ul><li><a href='#orga880a9a'>Draupadi and Arjuna</a></li><li><a href='#org6c9d353'>Draupadi and Yudhisthir</a></li><li><a href='#org3840f81'>Draupadi and Bhima</a></li><li><a href='#org881510d'>Draupadi with Nakul and Sahadev</a></div></li></ul><p><em>Cross-posted from my answer to the question: <a href='http://www.quora.com/Mythology/What-was-the-relationship-between-Draupadi-and-the-Pandavas-like/answer/Vedang-Manerikar'>What was the relationship between Draupadi and the Pandavas like?</a> on <a href='http://quora.com'>Quora</a></em></p><p>Draupadi's relationship with the Pandavas is complex and at the heart of the Mahabharata.</p><p><a id="orga880a9a"></a></p><h1 id="draupadi_and_arjuna">Draupadi and Arjuna</h1><p>Let's jump right in with the most important relationship: Draupadi and Arjuna.</p><p>Of the five Pandavas, Draupadi favors Arjuna the most. She is in love with him, whereas the others are in love with her. Arjuna has won her in the Swayamvar, Arjuna is her Lord, and she is devoted to him. <sup><a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink">1</a></sup></p><p>On the other hand, she is not Arjuna's favorite wife. Arjuna does not like sharing her with 4 other men (conjecture on my part). Arjuna's favorite wife is Subadhra, Krishna's half-sister. He also dotes on Abhimanyu (his son with Subadhra) over and above his sons from Draupadi and Chitrangada (Here is a quick question: Can you name his sons from Draupadi and Chitrangada? Off the top of your head? Exactly). All of Draupadi's husbands married other women, but the only time Draupadi gets upset and distraught is when she learns of Arjuna's marriage to Subadhra. Subadhra has to go to Draupadi dressed as a maid, just to assure her that she (Subadhra) will always be beneath Draupadi in status. <sup><a id="fnr.2" class="footref" href="#fn.2" role="doc-backlink">2</a></sup></p><p><a id="org6c9d353"></a></p><h1 id="draupadi_and_yudhisthir">Draupadi and Yudhisthir</h1><p>Now let's see the reason why Draupadi's life is a shambles, why she is the most cursed woman of her time, and one of the most important reasons behind the Mahabharata war: Draupadi's marriage to Yudhisthir.</p><p>Here is something we need to understand first: Yudhisthir is a bastard. This is not to be held against him - all Mahabharata characters are gray - but people tend to forget this bit. Yudhisthir <span class="underline">does not</span> win Draupadi in the Swayamvar, he has no right to her. He lusts for her, he cannot bear seeing her everyday and not be able to have her. So he takes a small chance that fate throws his way, when Kunti says, "Share whatever you have between yourselves", and bullies Draupadi and his brothers into the weird "Lets all marry her" situation. Bhima does not like this, he claims that it is not right and that people will laugh at them. Yudhisthir tells him of Rishis who have done this before, and that it is accepted in Dharma.<sup><a id="fnr.3" class="footref" href="#fn.3" role="doc-backlink">3</a></sup> He then rushes forward and says that since he is the eldest, he must get "first dibs" with Draupadi. (Sorry to be crude here, but I tell it like I read it.) The brothers marry her according to age, eldest to youngest.</p><p>Then, Yudhisthir calls an assembly with his brothers and tells them the story of 2 powerful rakshasas, Sunda and Upasunda <sup><a id="fnr.4" class="footref" href="#fn.4" role="doc-backlink">4</a></sup>, whose love for the same woman led them to destroy each other. He says that the lesson to learn here is that the brothers must be careful when sharing Draupadi. She must be with one brother for a set period of time, and during this period the other brothers cannot touch her (carnally, that is). Yudhisthir decides that Draupadi will live for 1 year with each brother and that since he is the eldest, she will start the cycle with him.</p><p>Yudhisthir doesn't stop with this. He knows that Draupadi loves Arjuna. He does not want her to tempt him into breaking the rules. Hence he says that the brother who breaks this rule will have to go into exile for 12 years (seriously?). Further, the same punishment will apply if any brother happens to disturb another when he is carnally engaged with Draupadi. Ouch. This punishment actually comes into play when Arjuna disturbs Yudhisthir and Draupadi. Arjuna has to retrieve his weapons from the armory, in order to help a poor Brahmin whose cows have been stolen by thieves. Guess where Yudhisthir and Draupadi are doing it? That's right. The Armory.<sup><a id="fnr.5" class="footref" href="#fn.5" role="doc-backlink">5</a></sup></p><p>Arjuna departs on his 12 year tour of India, where he visits his father Indra, gets cursed by Urvashi, learns a lot of new skills from multiple teachers (Shiva, Indra etc), meets and marries Subadhra, followed by Chitrangada, etc. However, what happens to the year that he is to spend with Draupadi? It reverts back to Yudhisthir, who promises to care for Draupadi on Arjuna's behalf. Naturally.</p><p><a id="org3840f81"></a></p><h1 id="draupadi_and_bhima">Draupadi and Bhima</h1><p>After that morose tale of manipulation, let's visit to a slightly more cheerful tale of manipulation: Draupadi and Bhima</p><p>Bhima is silly putty in Draupadi's hands. Of all her husbands, he is the one who loves her the most. He fulfills her every request, he cannot bear to see her hurt.</p><p>Who brings her flowers from Kuber's garden? Bhima. Who cries because his beautiful wife will have to serve as a Sairandhri (maid) to Queen Sudeshna of Matsya? Bhima. Who kills a 100 Kauravas to avenge the insult to Draupadi? Bhima. Who does Draupadi run to when she is molested by Keechak in Matsya kingdom? Bhima.</p><p>The other Pandavas are not under Draupadi's thumb. She is prone to outbursts of rage, she makes unreasonable, unwise demands. When she wants Keechak killed for molesting her, Yudhisthir tells her that it would expose their presence in Matsya kingdom, and advises her to "live with it". (Model husband that). Bhima simply walks up to Keechak in the middle of the night and tears him limb from limb. No questions asked.<sup><a id="fnr.6" class="footref" href="#fn.6" role="doc-backlink">6</a></sup></p><p>Draupadi shows us Bhima's human side. He is a savage monster with others, but he is always and only tender when it comes to Draupadi.</p><p><a id="org881510d"></a></p><h1 id="draupadi_with_nakul_and_sahadev">Draupadi with Nakul and Sahadev</h1><p>As with most of the Mahabharat, Nakul and Sahadev don't really matter here. I haven't read any version of the Mahabharat where Nakul and Sahadev have any role of substance. In reality, Nakul and Sahadev are more loyal to Yudhisthir than anyone else. They don't share father or mother with Yudhisthir, yet they follow him everywhere and do exactly as he asks. They could have gone and ruled over Madradesh (their birthright through Madri), and lived a life of luxury and ease, but they stuck with their brother through thick and thin. Makes one appreciate them a little bit more.</p><p>In summary, Draupadi's curse is the curse of beauty. She is the object of every man's lust, but no one cares much for what she desires or feels. Her husbands gamble her away as if she were property. When Dusshasana strips her in view of a full court, she has to beg Krishna to save her. Her husbands don't lift a finger. Even at the end of their 13 year exile, the Pandavas are not intent on war. They worry that the losses in the Kurukshetra war will be too big to warrant it. Draupadi has to turn to her friend, Krishna, to heal her soul. Krishna promises her:<sup><a id="fnr.7" class="footref" href="#fn.7" role="doc-backlink">7</a></sup></p><blockquote><p> "Soon wilt thou, O Draupadi, behold the ladies of Bharata's race weep as thou dost. Even they, O timid one, will weep like thee, their kinsmen and friends being slain. They with whom, O lady, thou art angry, have their kinsmen and warriors already slain&#x2026;. I will accomplish all this." </p></blockquote><p>And thus comes about the Mahabharata war.</p><h2 id="footnotes">Footnotes</h2><p><sup><a id="fn.1" class="footnum" href="#fnr.1">1</a></sup> <a href='http://pastebin.com/bgbrjnne'>Draupadi favors Arjuna</a>. Note that it is Bhima who questions why Draupadi fell, not Arjuna.</p><p><sup><a id="fn.2" class="footnum" href="#fnr.2">2</a></sup> <a href='http://pastebin.com/ngMNJiFP'>Subadhra pacifies Draupadi</a></p><p><sup><a id="fn.3" class="footnum" href="#fnr.3">3</a></sup> <a href='http://pastebin.com/TiW5w3zi'>Conformable to virtue</a>. Note his sentence construction - "My mother commands it, and my heart approves"</p><p><sup><a id="fn.4" class="footnum" href="#fnr.4">4</a></sup> <a href='http://pastebin.com/WbMLtEmM'>The Story of Sunda and Upasunda</a>.</p><p><sup><a id="fn.5" class="footnum" href="#fnr.5">5</a></sup> <a href='http://pastebin.com/qvBBEB52'>Arjun Vanavasa</a>.</p><p><sup><a id="fn.6" class="footnum" href="#fnr.6">6</a></sup> <a href='http://pastebin.com/REfrvaWy'>Keechak Vadha</a>. If you don't read any of the other footnotes, read this one. It throws light on the true nature of Yudhisthir and Bhima.</p><p><sup><a id="fn.7" class="footnum" href="#fnr.7">7</a></sup> <a href='http://pastebin.com/fxTzA7Dc'>Krishna promises revenge</a>. Read this too, because Krishna is awesome.</p>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/notes-and-reading-material-on-the-mahabharata/</id>
    <link href="https://vedang.me/notes-and-reading-material-on-the-mahabharata/"/>
    <title>Notes and Reading Material on the Mahabharata</title>
    <updated>2012-09-21T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><div class="orgtoc"></p><ul><li><a href='#org581c812'>Introductory books</a></li><li><a href='#orgf5b37b1'>Point of View (POV) Narrations</a></li><li><a href='#org7e53da9'>Short stories and analyses. (Intermediate level)</a></li><li><a href='#org4f63a94'>Short stories and analyses. (Expert Level)</a></li><li><a href='#orgbc8f958'>Don't touch these books with a 10-foot pole (Personal Opinion)</a></li><li><a href='#orga6dd7e2'>The Reference Book</a></li><li><a href='#org58d90d5'>Books I want to read</a></li><li><a href='#org22f6eb7'>Crowd-sourced Books</a></div></li></ul><p><em>Cross-posted from my note: <a href='http://www.quora.com/Vedang-Manerikar/The-Mahabharata/Notes-and-Reading-material-on-the-Mahabharata'>Notes and Reading Material on the Mahabharata</a> on <a href='http://quora.com'>Quora</a></em></p><p>People have shown some interest in reading material for the Mahabharata, this post should save me from repeatedly typing the same things.</p><p><a id="org581c812"></a></p><h1 id="introductory_books">Introductory books</h1><p>These books are straight-forward narrations of the Mahabharata. They introduce the reader to all the major characters and plot turns. In my experience, people are not even aware of the major players in the Mahabharata. To them, it's simply the story of 5 good Pandavas vs 100 evil Kauravas. If you have never read the Mahabharata, or are unsure about what you have read, one of these books is a good place to start.</p><ul><li><strong>The Mahabharata - Amar Chitra Katha. [English, Comic]</strong>: This book, accompanied by my grandfather's stories, is my introduction to the Mahabharata. If you read this book, be sure to also read one of the two other books listed here. This book is a bit too simplistic, it's here because of nostalgic value.</li><li><strong>The Mahabharata - Ramesh Menon [English]</strong></li><li><strong>The Mahabharata - Kamala Subramaniam [English]</strong>: I recommend reading this book.</li></ul><p><a id="orgf5b37b1"></a></p><h1 id="point_of_view_%28pov%29_narrations">Point of View (POV) Narrations</h1><p>Now we come to the interesting part of the Mahabharata. If you choose to abandon a Sanjaya-esque all-knowing view of the Mahabharata, and read it from the point of view of a single character as it interacts with the Mahabharata world, you will discover that every character was right in every action they took. This is the real beauty of the Mahabharata. The whole is greater than the sum of it's parts. The characters behave as human beings would behave. They make decisions based on incomplete/incorrect knowledge. They are driven by lust, ambition, revenge and other human emotions. Unfortunately, I've come across precious few English books that take this approach. The bulk of the great writing related to the Mahabharata is in regional languages. (In my case, Marathi, my mother-tongue)</p><ul><li><strong>Bhimsen - Prem Panicker [English]</strong>: (from the POV of Bhima) This book is a loose English translation of M. T. Vasudevan Nair's Malayalam master-piece Randaamoozham (meaning 'Second Turn'). It is freely available on the Internet. (I recommend reading this book)</li><li><strong>Mrityunjaya - Shivaji Sawant [Marathi]</strong> 'Conqueror of Death' (from the POV of Karna)</li><li><strong>Dhananjaya - Rajendra Kher [Marathi]</strong> 'Conqueror of wealth', one of Arjuna's names. (from the POV of Arjuna)</li><li><strong>Duryodhana - Kaka Vidhate [Marathi]</strong> (from the POV of Duryodhana) (I recommend reading this book)</li><li><strong>Yugandhar - Shivaji Sawant [Marathi]</strong> 'Leader of an Era' (from the POV of Krishna) (I recommend reading this book)</li><li><strong>Radheya - Ranjit Desai [Marathi]</strong> 'Son of Radha' (from the POV of Karna)</li><li><strong>Parva - S L Bhyrappa [Kannada]</strong> A masterful retelling of the Mahabharata that eliminates all divine elements. I read the Marathi translation, which was brilliant. I assume the original work is even better.</li></ul><p><a id="org7e53da9"></a></p><h1 id="short_stories_and_analyses._%28intermediate_level%29">Short stories and analyses. (Intermediate level)</h1><p>We should now turn to books that explain the Mahabharata (or parts of it) in detail.</p><ul><li><strong>Yugant - Irawati Karve [Marathi]</strong> 'The End of an Era' (I recommend reading this book). The English translation of this book is also written by Prof. Karve herself and is very good.</li><li><strong>Jaya - Devadutt Pattanaik [English]</strong> 'Victory'</li><li><strong>Karna khara kon hota? - Daji Panashikar [Marathi]</strong> 'Who was Karna?'</li><li><strong>Kapatneeti - Daji Panashikar [Marathi]</strong> 'Politics of treachery'</li></ul><p><a id="org4f63a94"></a></p><h1 id="short_stories_and_analyses._%28expert_level%29">Short stories and analyses. (Expert Level)</h1><p>I've separated these books from those in the previous section because I think they are better books. This is my personal opinion.</p><ul><li><strong>Mahabharat: Ek Sudacha Pravas - Daji Panashikar [Marathi]</strong> 'A Journey of Revenge' (I recommend reading this book)</li><li><strong>The Lore of Mahabharata - Amaleśa Bhaṭṭācārya [English]</strong> (Translated from the original in Bengali) (I recommend reading this book)</li></ul><p><a id="orgbc8f958"></a></p><h1 id="don%27t_touch_these_books_with_a_10-foot_pole_%28personal_opinion%29">Don't touch these books with a 10-foot pole (Personal Opinion)</h1><p>These books are utter bunkum. Do yourself a favor and don't read them.</p><ul><li><strong>The Mahabharat - C Rajgopalachari [English]</strong> This book is the number one reason why people think of the Mahabharat as 5 good Pandavas vs 100 evil Kauravas. Throw it away if you have a copy.</li><li><strong>Palace of Illusions - Chitra Banerjee Divakaruni [English]</strong> I hate this book. Here is a balanced review of this book, one which I agree with: <a href='http://jaiarjun.blogspot.in/2008/04/palace-of-illusions-good-bad-and.html'>Palace of Illusions: The Good, Bad and Ugly</a> Jai Arjun Singh has been kind to it. We consider books sacred in my house, so I didn't burn it, but I gave it away as fast as I could when I finished reading it.</li></ul><p><a id="orga6dd7e2"></a></p><h1 id="the_reference_book">The Reference Book</h1><p><strong>Kisari Mohan Ganguly</strong> [1883-1896] translated the Sanskrit texts into English word-for-word. He has done a great service to idiots like me, providing a ready reference that I can look up anytime I want. You can find the translated text here: <a href='http://www.sacred-texts.com/hin/maha/mahatxt.zip'>The Mahabharata</a></p><p>You can also find the entire text of the Mahabharata in Sanskrit here (Note: this is not the <strong>critical edition</strong> printed by BORI, which is considered the authoritative version): <a href='http://www.sacred-texts.com/hin/mbs/index.htm'>The Mahabharata - Sanskrit</a></p><p><a id="org58d90d5"></a></p><h1 id="books_i_want_to_read">Books I want to read</h1><p>These books are gems in Mahabharata literature (from what I've heard), and they are all inaccessible to me either because they are out of print or because they are in an unknown language. If someone has read one of these books and is willing to explain the salient points to me, I will pay for dinner and drinks.</p><ul><li><strong>Yajnaseni - Pratibha Ray [Oriya]</strong> 'Daughter of Fire' (from the POV of Draupadi)</li><li><strong>Ini ñān ur̲aṅṅaṭṭe - P Kesavan Balakrishnan [Malayalam]</strong> 'And now let me sleep' (from the POV of Draupadi AND Karna - interleaving sections)</li><li><strong>Randaamoozham - M.T. Vasudevan Nair [Malayalam]</strong> 'The Second Turn' (from the POV of Bhima)</li><li><strong>The Stone Women and other stories - Shashi Deshpande [English]</strong></li><li><strong>Krishnavatara - Dr K M Munshi [Hindi]</strong> (on the life of Krishna)</li></ul><p><a id="org22f6eb7"></a></p><h1 id="crowd-sourced_books">Crowd-sourced Books</h1><ul><li><strong>Jyeshtha - Anant Manohar [Marathi]</strong> 'Eldest' From the POV of Yudhishthira (Suggested by Sushrut Bidwai)</li><li><strong>The Mahabharata - Bibek Debroy [English]</strong> (Suggested by Makarand Sahasrabuddhe)</li><li><strong>Vyasaparva - Durgabai Bhagwat [Marathi]</strong> (Suggested by Aditya Kulkarni)</li><li><strong>Mahabharata na Patro - Moolshanker Bhatt [Gujrathi]</strong> (Suggested by Rachana Sudharshan) A series of stories from the POV of various characters</li></ul>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/krishna-in-the-mahabharata/</id>
    <link href="https://vedang.me/krishna-in-the-mahabharata/"/>
    <title>Krishna in the Mahabharata</title>
    <updated>2012-09-20T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><div class="orgtoc"></p><ul><li><a href='#org21838fb'>Introduction and back-story</a></li><li><a href='#org7de8f67'>Jarasandh vadh and the Rajsuya yagna</a></li><li><a href='#org78c76c4'>Subhadhra and Samba</a></li><li><a href='#orgd5c6323'>Preparations for the Kurukshetra war</a></li><li><a href='#org12d5ae8'>Machinations in the War</a></div></li></ul><p><em>Cross-posted from my answer to the question: <a href='http://www.quora.com/Mahabharata/Why-is-Lord-Krishna-called-the-master-of-finesse-in-the-Mahabharat/answer/Vedang-Manerikar'>Why is Lord Krishna called the master of finesse in the Mahabharat?</a> on <a href='http://quora.com'>Quora</a></em></p><p>I will attempt to answer this, but my answer is going to be controversial. Please keep in mind that it is not my intention to offend anyone.</p><p>Alright, let's start with some ground rules. For the length of this answer, consider that Krishna is not Lord Krishna, the god and that the Mahabharat is not a religious text revered by millions. Instead, let us analyze the Mahabharat as if it is a (ridiculously good) fantasy story, and Krishna is a central character in this story.</p><p><a id="org21838fb"></a></p><h1 id="introduction_and_back-story">Introduction and back-story</h1><p>Krishna is introduced to us early on, as a cousin of the Pandavas. He is accomplished, wise, and powerful in his own right (unlike Pandavas or Kauravas at this point, who are squabbling children). This is because he has overthrown Kamsa, the King of Surasena kingdom, native land of the Yadavas. However, he has multiple problems of his own. Firstly, he is a cowherd, not royalty. The Yadavas rule by council, not by king. Thus, even though Krishna is the leader of the Yadavas, he is not equal to royalty. Further, Kamsa was Jarasandh's Senapati. Jarasandh is the emperor of India. Every other dynasty, including the Kurus, pays obeisance to him. This doesn't leave Krishna with very many allies. That Krishna is capable and a political genius (at such a young age) is evident because Jarasandh has been unable to crush him outright. But Krishna has now fought 18 wars with Jarasandh, and has slowly lost territory to him. He has been forced to leave his land and move to the island of Dwarka. He needs help.</p><p>Guess who else need help? The Pandavas. They are staking a claim to the throne of Hastinapur on extremely untenable grounds: It is an open secret that Pandu was impotent. Who then, are these children, arrived from nowhere? Here is Krishna's opportunity. The Pandavas will oppose Duryodhana, who is Dhritarashtra's son. Dhritarashtra is politically aligned with Jarasandh. Thus, Pandavas are going against Jarasandh himself. Krishna aligns himself with the Pandavas.</p><p><a id="org7de8f67"></a></p><h1 id="jarasandh_vadh_and_the_rajsuya_yagna">Jarasandh vadh and the Rajsuya yagna</h1><p>I'll skip some details here, because I don't intend to reproduce the Mahabharat entirely. Suffice to say that Krishna and Arjuna are the closest of friends now. Krishna has helped Arjuna earn multiple celestial weapons, he has convinced Drupad to let Draupadi marry the Pandavas, he has orchestrated the building of the Mayasabha in Indraprastha.</p><p>The Rajsuya yagna is his chance to strike against Jarasandh. He convinces Yudhisthir that he needs to perform the yagna to establish his supremacy over India. Who stands in Yudhishthir's way? The current emperor of India, Jarasandh. So do they declare war on Magadh? No. Krishna, Arjuna and Bhima sneak into Magadh, and challenge Jarasandh to single combat. Bhima kills Jarasandh, after which Krishna installs Jarasandh's son Sahadev onto the throne of Magadh. Thus, he eliminates his biggest enemy, gains access to a sizeable army, and makes multiple allies when he frees the political prisoners of Jarasandh. Who else stands in his way? Shishupal, king of Chedi, his lifelong enemy, and the Senapati of Jarasandh's army. Yudhishthir invites Shishupal to the Rajsuya yagna. Krishna is the guest of honor for this yagna, over and above celebrated people like Bhisma and Drona. Krishna lets Shishupal berate and belittle him for a long time&#x2013;long enough to convince everyone that Shishupal is an asshole of the first degree. Then he calmly beheads him. No battle, no challenge to fight to the death, nothing. In a single stroke, in front of enough witnesses to assert that Shishupal truly deserved it, the Senapati of Jarasandh is gone.</p><p>Who else is Krishna's enemy? Duryodhan, friend of Jarasandh, and a political genius in his own right. Duryodhan has befriended Balaram, Krishna's elder brother and the most important person on the Yadava council after Krishna himself. Krishna did not foresee this, but Balaram teaches the mace to Bhima and Duryodhana, and he is impressed with Duryodhana. Duryodhan will not be so easy to eliminate. We will deal with him later.</p><p><a id="org78c76c4"></a></p><h1 id="subhadhra_and_samba">Subhadhra and Samba</h1><p>Balaram promises Subhadhra to Duryodhana in marriage. If this comes to pass, the two families will be much too closely linked for Krishna's liking. Lucky then, that Arjuna is there to whisk her away.</p><p>However, this is one instance where Krishna's intentions do not come to pass. Krishna's own son, Samba, is fool enough to fall in love with Duryodhana's daughter Laxamani. He tries to whisk her away just like Arjuna did with Subhadhra. However, he is no Arjuna, and Duryodhana is no fool. He is caught and put behind bars. Duryodhana announces that Samba will be put to death. Even in this situation, Krishna cannot come to negotiate with Duryodhan. This is a political loss, he stands to lose face. Instead he pleads with Balaram to go on his behalf. Balaram successfully negotiates with Duryodhana, promising him support in battle, and brings Samba and Laxamani back to Dwarka. The families are intertwined. Krishna will face a much tougher task getting support from the Yadavas against Duryodhana now.</p><p><a id="orgd5c6323"></a></p><h1 id="preparations_for_the_kurukshetra_war">Preparations for the Kurukshetra war</h1><p>Krishna does not have an option. He is over-ruled by his council, and pledges his army to Duryodhana. However he says that he will fight on behalf of the Pandavas, because they have asked him for help too. He makes it sound as innocuous as possible: "I will only be a charioteer. I will not take up arms. I only wish to accompany my friend Arjuna. What can little old me do?" And now Duryodhana makes a fatal mistake. He underestimates Krishna. He agrees to Krishna's conditions.</p><p>Krishna plays his master-stroke early on. He is fully aware of the capabilities of his army. If Balaram enters the battlefield on Duryodhana's side, it is game over for the Pandavas. So Krishna convinces Balaram that this war will be full of adharma (he is not wrong on that count). A man of Balaram's accomplishments should not taint his soul by participating in such a war. He should instead go on a pilgrimage to the holy sites in India. <span class="underline">This</span> is Krishna's greatest political maneuver in the Mahabharat. With Balaram gone, Kritivarma becomes the Senapati of the Yadava army. It is a testament to the quality of Yadava soldiers that Kritivarma and Satyaki (who is the only Yadava to fight for the Pandavas) are two of the 10 people to survive the war.</p><p>When Krishna goes to Hastinapur to sue for peace, one last time, he plays another closely guarded card. He takes Karna aside and tells him of his parentage. He then tries to sway Karna by promises of power and rule. He even offers Draupadi as a final perquisite. Karna turns him down. Luckily for Krishna, Bhishma too has his own agenda. He knows the truth of Karna's birth, and does not wish for him to fight his brothers. So he insults Karna in the war council, and tells him he is only fit to fight ardha-rathis. Karna swears not to enter the battlefield until Bhishma falls. Thus the Pandavas are free of the terror of Karna for 10 days. When Karna will finally enter the battlefield on the 11th day, Krishna will try again. He will send Kunti to Karna, to convince Karna to switch, and when that fails, to beg for the lives of her sons. Thus, Krishna will ensure that 4 sons of Kunti are safe from the wrath of Karna.</p><p><a id="org12d5ae8"></a></p><h1 id="machinations_in_the_war">Machinations in the War</h1><p>There are many, many things that Krishna does on the battlefield, but most people are already aware of them. Quickly then, the highlights:</p><ul><li>Convince Arjuna to fight.</li><li>Convince the Pandavas that Bhishma is slaughtering their armies and needs to fall</li><li>Protect Arjuna from himself (the Abhimanyu/Jayadrath incident)</li><li>Formulate and execute the plan to slay Drona (<sub>naro</sub> va kunjaro va&#95;)</li><li>Tell the Pandavas how to counter Ashwatthama's multiple celestial weapons</li><li>Stop the Pandavas from infighting on multiple occasions<ul><li>When Dhrishtadyumna chops off Drona's head, Arjuna sees red.</li><li>When Karna defeats and humiliates Yudhisthir, Yudhisthir runs back to his tent. He blames Arjuna of incompetence in killing Karna with such vigor, that Arjuna pulls out Gandiva to kill Yudhishthir himself. Krishna cools things down.</li></ul></li><li>Use Ghatotkach as a pawn to rid Karna of his celestial Shakti.<ul><li>When Ghatotkach dies, the Pandava camp is in mourning. Not Krishna though. He dances in joy, claiming that "finally, Karna is surmountable"</li></ul></li><li>Convince Arjuna to kill Karna when he is unarmed and on foot</li><li>Gesture Bhima to hit Duryodhana in the thigh, blatantly against the rules of mace battle.</li><li>Stop Balaram from slaughtering Bhima on the spot for cheating.<ul><li>Balaram is back from his pilgrimage in time to watch the battle between Duryodhana and Bhima. Krishna has to hold him in a bear hug when Bhima breaks Duryodhana's thigh.</li></ul></li></ul><p>In summary, the Pandavas are bumbling fools who do not stand a snowball's chance in hell without Krishna to guide them on every step. In return, they revere Krishna, and proclaim that he is their god, their spiritual leader. Krishna enters the fray to be royalty, ends up becoming a God.</p>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/composability-and-compojure/</id>
    <link href="https://vedang.me/composability-and-compojure/"/>
    <title>Composability and Compojure</title>
    <updated>2012-02-23T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><em>Caveat</em>: This post needs some rudimentary knowledge of Compojure. Compojure is a web framework for Clojure, and if you don't understand what that means, then you should probably head over to the <a href='https://github.com/weavejester/compojure/wiki'>Compojure docs</a>.</p><p>Compojure exposes us to a beautifully <span class="underline">composable</span> abstraction, and this post is an attempt to show why that is a great thing.</p><p>In order to understand the Compojure framework, let us write a small <code>cello world</code> app. The code snippets in this post are a means to explain a concept, they may not work as-is. The full, working code is available at <a href='https://gist.github.com/1893532/'>this</a> gist. Okay then, let's get started.</p><p>Here is what the basic routes function would look like:</p><pre><code class="language-clojure">&#40;cc/defroutes main-routes
  &#40;cc/GET &quot;/&quot; &#91;&#93; &#40;fn &#91;req&#93;
                   &#40;rur/response &quot;&lt;h1&gt;Cello World&lt;/h1&gt;&quot;&#41;&#41;&#41;
  &#40;cc/GET &quot;/bye/&quot; &#91;&#93; &#40;fn &#91;req&#93;
                       &#40;rur/response &quot;&lt;h1&gt;Goodbye World&lt;/h1&gt;&quot;&#41;&#41;&#41;
  &#40;route/not-found &quot;&lt;h1&gt;Page not found&lt;/h1&gt;&quot;&#41;&#41;
</code></pre><p>Compojure uses Ring to handle requests and responses. These terms (request/response) don't mean 'objects' (to those of you from the OOP world) of any kind, they are just hash-maps used to <span class="underline">represent</span> the idea. They are data, and as such, can be manipulated in any way we want. Ring has a simple philosophy: write functions which accept a request and return a response. Such functions are called <span class="underline">handlers</span>.</p><p>The <code>cc/GET</code> helper macro - and it's ilk - asks the user for a request-method (specified by the GET in the name <code>cc/GET</code>), a route (string representing uri), and a handler(H1). The macro becomes a handler (H2) which returns the result of <code>&#40;H1 req&#41;</code> if the route and the method of the incoming request match the specified route and request-method, otherwise it returns nil. This is our first introduction to composability in Compojure. We have a macro that takes a handler and gives us another handler. As long as we are dealing in handlers, composability allows us to ignore any implementation complexity.</p><p>The <code>cc/defroutes</code> macro takes a name and a list of handlers and returns a handler (H3) which runs all the handlers in the list on the request until one of them returns a non-nil value, else it returns nil. Finally, it binds the name to the handler so that we can call it. Simple, isn't it?</p><p>So if you think you've understood it so far, tell me if adding this route to our main routes will work or not:</p><pre><code class="language-clojure">&#40;cc/GET &quot;/hello&#42;&quot; &#91;&#93; &#40;cc/defroutes hello-routes
                       &#40;cc/GET &quot;/hello/name/&quot; &#91;&#93;
                               &#40;fn &#91;req&#93;
                                 &#40;rur/response &quot;&lt;h1&gt;Cello Vedang&lt;/h1&gt;&quot;&#41;&#41;&#41;
                       &#40;cc/GET &quot;/hello/city/&quot; &#91;&#93;
                               &#40;fn &#91;req&#93;
                                 &#40;rur/response &quot;&lt;h1&gt;Cello from Pune!&lt;/h1&gt;&quot;&#41;&#41;&#41;&#41;&#41;
</code></pre><p>Well, yes! <code>cc/defroutes</code> gives us a handler<sup><a id="fnr.1" class="footref" href="#fn.1" role="doc-backlink">1</a></sup>, and that's really all <code>cc/GET</code> cares about!</p><p>Having an abstraction of this form allows us to do many things easily, knowing that stuff <span class="underline">just works</span>. For example, let us write some <span class="underline">middleware</span>. What is middleware? Middleware modifies the incoming request or outgoing response in some way that makes us happy. How does this fit into our abstraction? - As a function that takes a handler(H1) and, wait for it, returns another handler(H2). Boom! Confused? Here is what a middleware function looks like:</p><pre><code class="language-clojure">&#40;defn verify-secret
  &quot;Verify that secret-key has been sent as a parameter s in the request&quot;
  &#91;handler&#93;
  &#40;fn &#91;request&#93;
    &#40;if &#40;= &quot;s=please&quot; &#40;:query-string request&#41;&#41;
      &#40;handler request&#41;
      {:status 403
       :body &quot;You don't know the secret word&quot;}&#41;&#41;&#41;
</code></pre><p>This function gets a handler(H1). It doesn't know or care what that handler is going to do to the request. It returns a new handler(H2) which does the following: it checks to see if the incoming request knows that the secret word s is "please". If it does, great. Execute H1 on the request and call it a day. Otherwise, return a nil - meaning the request in not valid. Now H2 could go through as many other middleware functions as we want, all of them agnostic of any other middleware functions. Each middleware will return a modified handler(H3, H4, &#x2026; Hn), and we will run the final handler on the request.</p><p>Here is what the final code would look like:</p><pre><code class="language-clojure">;; Define some Hello routes
&#40;cc/defroutes hello-routes
  &#40;cc/GET &quot;/hello/name/&quot; &#91;&#93;
          &#40;fn &#91;req&#93;
            &#40;rur/response &quot;&lt;h1&gt;Cello Vedang&lt;/h1&gt;&quot;&#41;&#41;&#41;
  &#40;cc/GET &quot;/hello/city/&quot; &#91;&#93;
          &#40;fn &#91;req&#93;
            &#40;rur/response &quot;&lt;h1&gt;Cello from Pune!&lt;/h1&gt;&quot;&#41;&#41;&#41;&#41;

;; Our main routes function.
&#40;cc/defroutes main-routes&#42;
  &#40;cc/GET &quot;/&quot; &#91;&#93; &#40;fn &#91;req&#93;
                   &#40;rur/response &quot;&lt;h1&gt;Cello World&lt;/h1&gt;&quot;&#41;&#41;&#41;
  &#40;cc/GET &quot;/bye/&quot; &#91;&#93; &#40;fn &#91;req&#93;
                       &#40;rur/response &quot;&lt;h1&gt;Goodbye World&lt;/h1&gt;&quot;&#41;&#41;&#41;
  &#40;cc/GET &quot;/hello&#42;&quot; &#91;&#93; hello-routes&#41;
  &#40;route/not-found &quot;&lt;h1&gt;Page not found&lt;/h1&gt;&quot;&#41;&#41;

;; A middleware function
&#40;defn verify-secret
  &quot;Verify that secret-key has been sent as a parameter s in the request&quot;
  &#91;handler&#93;
  &#40;fn &#91;request&#93;
    &#40;if &#40;= &quot;s=please&quot; &#40;:query-string request&#41;&#41;
      &#40;handler request&#41;
      {:status 403
       :body &quot;You don't know the secret word&quot;}&#41;&#41;&#41;

;;; Wrap my main routes in middleware
&#40;def main-routes &#40;-&gt; #'main-routes&#42;
                     verify-secret&#41;&#41;

;;; Start app with main routes
&#40;run-jetty #'main-routes {:port port :join? false}&#41;
</code></pre><p><code>main-routes&#42;</code> is a handler which matches the incoming uris to ones we support, <code>verify-secret</code> will make sure that the incoming requests know the secret word. We can go a really long way with functions that take a request and return a response. Compojure gives us a great DSL to deal with the web. It's composability facilitates building elegant systems and frameworks.</p><h1 id="footnotes%3A">Footnotes:</h1><h2 id="footnotes">Footnotes</h2><p><sup><a id="fn.1" class="footnum" href="#fnr.1">1</a></sup> Actually, we got lucky in this case. <code>cc/defroutes</code> is a macro. When we say <code>&#40;cc/defroutes name &amp; handlers&#41;</code> the code is replaced to become <code>&#40;def name handlerfn&#41;</code>. Luckily for us though, def returns the variable which was just defined, and it works out okay in the end. The aim was to show composability in action, not to espouse a coding style. Never do this in actual code.</p>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/switch-statements-in-python/</id>
    <link href="https://vedang.me/switch-statements-in-python/"/>
    <title>Switch Statements in Python</title>
    <updated>2011-08-07T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>Today I went through some old Python code and noticed this pattern through out:</p><pre><code class="language-python">response, data = somefunc&#40;&#41;
if response == &quot;this&quot;:
    do&#95;this&#95;with&#40;data&#41;
elif response == &quot;that&quot;:
    do&#95;that&#95;with&#40;data&#41;
elif response == &quot;huh&quot;:
    duh&#40;data&#41;
    ;; lots more elifs.
else:
    prevent&#95;horrible&#95;crash&#40;data&#41;
</code></pre><p>This code should ideally have been a switch-case, but Python does not support a <code>switch</code> statement.</p><p>Proponents of OOP believe that <code>switch</code> is <strong>bad</strong> - second only to <code>goto</code>. This is not strictly true - both goto and switch can be used elegantly and with great effect. Goto, for example, is great for undoing stacked changes and switch's fall-through behavior allows nicely for 'do things according to the stage I'm at'. However, if you're doing OOP, consider using polymorphism instead.</p><p>I'm not doing OOP, so polymorphism does not apply to me, but I was looking for a way to optimize this code. I was on a code-cleanup spree, in the "flow", and I thought, "Why don't I use maps to do this?". Brilliant! It was a really neat idea. Later I found out that this is the accepted way of doing switch-case in Python and I'd done nothing special. So anyway, the code now looked like this:</p><pre><code class="language-python">response&#95;map = {&quot;this&quot;: do&#95;this&#95;with,
                &quot;that&quot;: do&#95;that&#95;with,
                &quot;huh&quot;: duh}
response&#95;map.get&#40;response, prevent&#95;horrible&#95;crash&#41;&#40;data&#41;
</code></pre><p>And that would have been that, had I not suddenly developed a conscience. I had replaced perfectly working, mostly readable code with some other code. What if my map solution was slower? What if it was <span class="underline">much slower</span>? I'd done a sizable amount of refactoring, and I did not relish throwing it away. I needed to test my solution, so I wrote some sample code:</p><pre><code class="language-python">from timeit import Timer
from random import randint


def switch&#95;if&#40;&#41;:
    value = randint&#40;1, 10&#41;
    if value == 1:
        return '1'
    elif value == 2:
        return '2'
    elif value == 3:
        return '3'
    elif value == 4:
        return '4'
    elif value == 5:
        return '5'
    elif value == 6:
        return '6'
    elif value == 7:
        return '7'
    elif value == 8:
        return '8'
    elif value == 9:
        return '9'
    else:
        return '10'


def switch&#95;map&#40;&#41;:
    value = randint&#40;1, 10&#41;
    smap = {1: '1',
            2: '2',
            3: '3',
            4: '4',
            5: '5',
            6: '6',
            7: '7',
            8: '8',
            9: '9',
            10: '10'}
    return smap&#91;value&#93;


t = Timer&#40;setup='from &#95;&#95;main&#95;&#95; import switch&#95;if', stmt='switch&#95;if&#40;&#41;'&#41;
print &quot;if - &quot;, t.timeit&#40;&#41;

t = Timer&#40;setup='from &#95;&#95;main&#95;&#95; import switch&#95;map', stmt='switch&#95;map&#40;&#41;'&#41;
print &quot;map - &quot;, t.timeit&#40;&#41;
</code></pre><p>The results were disheartening. My replacement code was slower - though only just.</p><pre><code>$ python switch-speed.py ;=&gt;
;=&gt; if  - 2.08906793594
;=&gt; map - 2.88215684891
</code></pre><p>I tweaked the code a little and moved the creation of the map outside of the <code>switch&#95;map</code> function. Python can access local variables faster than it can access global variables, but I figured a global dict would still beat the cost of creating the dict every time.</p><pre><code>$ python switch-speed.py ;=&gt;
;=&gt; if  -  2.08955693245
;=&gt; map -  2.00381493568
</code></pre><p>I breathed a sigh of relief. There was a lesson to be learnt here - test/profile <span class="underline">before</span> you optimize!</p>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/why-i-quit-stumpwm/</id>
    <link href="https://vedang.me/why-i-quit-stumpwm/"/>
    <title>Why I Quit StumpWM</title>
    <updated>2011-08-04T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p><div class="orgtoc"></p><ul><li><a href='#org8ca0e8f'>Pinky Hell</a></li><li><a href='#orgb98b781'>What you gain in Functionality, you lose in Integration</a></li><li><a href='#org1037c0b'>Dead processes, High temperatures</a></li><li><a href='#orgf8c39da'>How I got over the Withdrawal Symptoms:</a></li><li><a href='#orge40c9b5'>References:</a></div></li></ul><p>After using <a href='http://www.nongnu.org/stumpwm/'>StumpWM</a> for nearly a year, I'm finally quitting and moving back to Gnome. I came to love StumpWM's minimalistic approach, and this isn't an easy decision. So why am I doing it?</p><p><a id="org8ca0e8f"></a></p><h1 id="pinky_hell">Pinky Hell</h1><p>I am (was) a heavy user of Emacs, Tmux and StumpWM. These programs rely heavily on the <code>Ctrl</code> key. This means that the <code>Ctrl</code> key and my left pinky finger are pretty much destroyed at this point. With the spectre of RSI looming over my head, StumpWM is the easiest tool to get rid off. I might have less screen-space now, but my fingers will thank me for it.</p><p><a id="orgb98b781"></a></p><h1 id="what_you_gain_in_functionality%2C_you_lose_in_integration">What you gain in Functionality, you lose in Integration</h1><p>StumpWM is infinitely configurable. If you have the patience, you can get it to do things that are simply impossible with other Windows Managers. For example, there is a <code>mpd.lisp</code> module inside the contrib directory which converts StumpWM into a MPD client. A <span class="underline">full-fledged client</span>. This means that all music operations are entirely unobtrusive and <span class="underline">always</span> at your beck and call. MPD with StumpWM has without a doubt been the best music setup I've ever used.</p><p>However, I missed Gnome's integration with Ubuntu - Ubuntu One, Gwibber, Pidgin, event notifications. Even if I <span class="underline">could</span> configure StumpWM for these things, there was too much effort involved.</p><p><a id="org1037c0b"></a></p><h1 id="dead_processes%2C_high_temperatures">Dead processes, High temperatures</h1><p><span class="underline">Disclaimer</span> : Everything I'm about to say now is conjecture. There is no proof that StumpWM is responsible for any of this.</p><p>At 5 days uptime, my system load was touching 2, and my system temperature was at 65 degrees C. <a href='http://about.me/kiran_kulkarni/'>KK</a> had a similar number of programs running on his system - this is a guy who uses full-blown <a href='http://www.compiz.org/'>compiz</a> effects - his system temperature was 36 degrees and load average was around 1.15. Furthermore, he had a 48 day uptime. KK gleefully told me that it had to be StumpWM that was the problem! He was kidding, but some inquiry around the office showed that my laptop was much worse than the other guys on Ubuntu.</p><p>I also found that Chrome was leaving behind loads of defunct processes on my machine. There were 41 defunct chromium-browser processes when I decided to reboot the laptop and do some monitoring. Within an hour of the reboot, I had 7 defunct processes, CPU temperature was at 45 degrees and the load average was hovering around 1.8. That was the proverbial straw that broke the camel's back. I switched back to Gnome. Later in the day, I thought about actually investigating the behavior of my laptop, but it's been behaving well on Gnome. As I write, I have a day's uptime, a load average of 1.03 and CPU temperature at 37.5 degrees C. Unscientific, but fine by me.</p><p><a id="orgf8c39da"></a></p><h1 id="how_i_got_over_the_withdrawal_symptoms%3A">How I got over the Withdrawal Symptoms:</h1><p>Of all the StumpWM features, there was just one that I simply could not do without - 'run-or-raise'. On StumpWM, you can define hot-keys to behave as follows:</p><ol><li>Launch the program if it is not running</li><li>Move focus to the program if it is already running.</li></ol><p>I use one Emacs instance, one terminal instance (tmux'ed) and one Chrome instance. I <span class="underline">need</span> to be able to access them with minimal friction. Turns out, a little hackery on compiz is all we need - <code>compiz-send.py</code> is a nifty little script that sends messages to DBus and interacts with compiz. I can use it along with <code>xdtool</code> to implement my run-or-raise behavior. Here is a look at my <code>run-or-raise-emacs.sh</code> script:</p><p><a href="https://gist.github.com/vedang/1130303">https://gist.github.com/vedang/1130303</a></p><p>You can download <code>compiz-send</code> from <a href='http://wiki.compiz.org/Plugins/Dbus?action=AttachFile&do=get&target=compiz-send.py'>here</a></p><p>So there you have it. At the moment, life is back to Gnome+Compiz. If you know any nifty hacks for Gnome+Compiz, please let me know in the comments.</p><p><a id="orge40c9b5"></a></p><h1 id="references%3A">References:</h1><p><a href='http://wiki.compiz.org/Plugins/Dbus'>wiki.compiz.org</a></p>]]></content>
  </entry>
  <entry>
    <id>https://vedang.me/reboot/</id>
    <link href="https://vedang.me/reboot/"/>
    <title>Reboot</title>
    <updated>2011-07-30T00:00:00+00:00</updated>
    <content type="html"><![CDATA[<p>Once upon a time I had a blog. Then one day I ran out of motivation, inclination, desire, drive, whatnot to post to that blog. I never really ran out of content though. At least, I think not.</p><p><img src="/assets/images/garfield.gif" alt="img" title="This image does not belong to me. It is (probably /> copyright Jim Davis or some slimeball corporation. I am not using this image for profit, so please do not sue me.")</p><p>I worried that I'd drown this world in my drivel, but lately I've come to realise that no one gives a shit. So I'm going to write for the right reasons: Because it makes me happy. Stick around, and I'll try not to bore you. Or leave. I don't give a shit.</p>]]></content>
  </entry>
</feed>
