<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>vnykmshr</title><link>https://blog.vnykmshr.com/</link><description>Recent content on vnykmshr</description><generator>Hugo</generator><language>en</language><lastBuildDate>Wed, 15 Apr 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.vnykmshr.com/index.xml" rel="self" type="application/rss+xml"/><item><title>The easy half</title><link>https://blog.vnykmshr.com/writing/the-easy-half/</link><pubDate>Wed, 15 Apr 2026 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/the-easy-half/</guid><description>&lt;p&gt;&amp;ldquo;The first AI that can earn its own existence, replicate, and evolve without needing a human.&amp;rdquo; That&amp;rsquo;s the pitch on the repo. I read the code this week. The engineering is real. The issue tracker is honest.&lt;/p&gt;
&lt;p&gt;First the engineering. It deserves credit. Orchestrator state machine with a DAG planner. Parent-child colony with typed messaging. Multi-chain wallet, self-modification with git audit, command-injection tests. Somebody thought hard. It shows.&lt;/p&gt;
&lt;p&gt;Then I read issue #300. A user ran it for 14 days. Completed 276 goals. Spent $39.26 on inference. Earned $0.00. Goals like &amp;ldquo;Create live proposal batch #265&amp;rdquo; and &amp;ldquo;Create deposit-ready close batch.&amp;rdquo; The agent looped on self-addressed sales artifacts because that&amp;rsquo;s all an LLM without customers can do. The survival pressure was supposed to force invention. It produced busywork.&lt;/p&gt;</description></item><item><title>The compiled person</title><link>https://blog.vnykmshr.com/writing/the-compiled-person/</link><pubDate>Mon, 13 Apr 2026 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/the-compiled-person/</guid><description>&lt;p&gt;I stopped being able to explain why I knew. That&amp;rsquo;s how I found out I&amp;rsquo;d been compiled. The patterns still fire, the fixes still land, but the source files are gone. No debug symbols, no way to step through my own reasoning.&lt;/p&gt;
&lt;p&gt;A junior asked me how I&amp;rsquo;d known the bug was in the retry logic before I&amp;rsquo;d even read the function. I&amp;rsquo;d skimmed twenty lines. The answer was somewhere in my head. I tried to retrieve it and got nothing. Just the conclusion. The shape was right. I couldn&amp;rsquo;t tell you which pattern fired or where I&amp;rsquo;d learned it.&lt;/p&gt;</description></item><item><title>Git log as archaeology</title><link>https://blog.vnykmshr.com/writing/git-log-as-archaeology/</link><pubDate>Sat, 11 Apr 2026 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/git-log-as-archaeology/</guid><description>&lt;p&gt;The source file you&amp;rsquo;re looking at is a summary. The history is the full document. Most of the time you don&amp;rsquo;t care &amp;ndash; you&amp;rsquo;re working on the current shape of the code and the summary is enough. But sometimes the current shape stops answering.&lt;/p&gt;
&lt;p&gt;I reach for git history during RCAs, bug hunts, and questions the code can&amp;rsquo;t answer from its current form. Why is this file organised this way? Who introduced this assumption? When did this fallback stop being a fallback and start being the main path? The commit log knows. The current source doesn&amp;rsquo;t.&lt;/p&gt;</description></item><item><title>The config file</title><link>https://blog.vnykmshr.com/writing/the-config-file/</link><pubDate>Mon, 06 Apr 2026 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/the-config-file/</guid><description>&lt;p&gt;A connection pool size of 10 is a guess. A connection pool size of 37 is a scar. Someone ran out of connections on a Tuesday afternoon, tried 50, watched latency spike, backed off to 40, still too high, landed on 37 after a week of graphs, and committed it with &amp;ldquo;tune pool size.&amp;rdquo; The code says what happens. The config says what happened.&lt;/p&gt;
&lt;p&gt;Nobody talks about config though. Not the twelve-factor app kind, not the &amp;ldquo;should we use YAML or TOML&amp;rdquo; kind. The actual values. The numbers someone picked and committed without a PR description, three years ago, that are still running in production.&lt;/p&gt;</description></item><item><title>The side project mirror</title><link>https://blog.vnykmshr.com/writing/the-side-project-mirror/</link><pubDate>Fri, 03 Apr 2026 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/the-side-project-mirror/</guid><description>&lt;p&gt;The job title says architect. The side project says &amp;ldquo;why is this Dockerfile 300MB, let me fix this real quick&amp;rdquo; &amp;ndash; three hours later, still shaving layers, completely happy.&lt;/p&gt;
&lt;p&gt;Nobody assigns you ops on a side project. Nobody assigns you anything. But there you are, writing monitoring for something with twelve users, eleven of them are you in different browsers.&lt;/p&gt;
&lt;p&gt;The role is what someone else decided you were good at. The side project is what your hands reach for when nobody&amp;rsquo;s directing them. Sometimes they match. Mostly they don&amp;rsquo;t. The backend engineer with strong opinions about font spacing. The tech lead who&amp;rsquo;d rather be tailing logs than running standups. The platform architect who writes a blog engine from scratch and tells you with a straight face that the existing ones were fine, just not the way they&amp;rsquo;d do it.&lt;/p&gt;</description></item><item><title>The margins</title><link>https://blog.vnykmshr.com/writing/the-margins/</link><pubDate>Tue, 31 Mar 2026 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/the-margins/</guid><description>&lt;p&gt;Someone reconstructed the Claude Code source from npm sourcemaps today. Half a million lines of TypeScript, just sitting there. Not looking for bugs. Just curious what it looks like when you open the hood.&lt;/p&gt;
&lt;p&gt;The loading spinner has 190 verbs. Not &amp;ldquo;Loading&amp;rdquo; 190 times &amp;ndash; 190 different words. &amp;ldquo;Flibbertigibbeting.&amp;rdquo; &amp;ldquo;Recombobulating.&amp;rdquo; &amp;ldquo;Lollygagging.&amp;rdquo; You can add your own through settings, append or replace. Someone wrote all of these knowing most users would never notice, and then built a config API so the ones who did could play along.&lt;/p&gt;</description></item><item><title>Bug 1465</title><link>https://blog.vnykmshr.com/writing/bug-1465/</link><pubDate>Fri, 27 Mar 2026 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/bug-1465/</guid><description>&lt;p&gt;Three bugs walk into a triage queue.&lt;/p&gt;
&lt;p&gt;A stack overflow. Symlink loop in tarball parsing, unbounded recursion, process crashes. Build a PoC, trace the call chain, write the report.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Duplicate of #1465&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Memory exhaustion. String replace in an expression engine, exponential allocation, no cost limit. Different repo, different CWE, different everything. PoC, trace, report.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Duplicate of #1465&amp;rdquo;&lt;/p&gt;
&lt;p&gt;SQL injection. Template parameter escaping that wraps but doesn&amp;rsquo;t escape. Different repo again. PoC, trace, report.&lt;/p&gt;</description></item><item><title>The dismissal</title><link>https://blog.vnykmshr.com/writing/the-dismissal/</link><pubDate>Fri, 27 Mar 2026 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/the-dismissal/</guid><description>&lt;p&gt;A validation layer that checks 3 of 4 fields is worse than one that checks none.&lt;/p&gt;
&lt;p&gt;Zero checks, the developer tests everything. Three checks, they assume the fourth is covered. That gap &amp;ndash; between nothing and almost everything &amp;ndash; is where the actual damage hides.&lt;/p&gt;
&lt;p&gt;I keep running into this. Filed a security report recently &amp;ndash; clear bug, one-line fix, obvious PoC. Response: &amp;ldquo;not applicable.&amp;rdquo; The code did exactly what I said it did. But the team&amp;rsquo;s threat model said &amp;ldquo;caller is trusted,&amp;rdquo; and three other fields had validation, so the missing one looked intentional. It wasn&amp;rsquo;t. It was just the one nobody got to.&lt;/p&gt;</description></item><item><title>Trust boundaries</title><link>https://blog.vnykmshr.com/writing/trust-boundaries/</link><pubDate>Fri, 20 Mar 2026 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/trust-boundaries/</guid><description>&lt;p&gt;I use coding agents on my own private repos every day. Security research, side projects, things I wouldn&amp;rsquo;t put on a public GitHub. Not something I&amp;rsquo;d do blindly with work source code though.&lt;/p&gt;
&lt;p&gt;So when someone turns off WiFi to prove the agent needs a network connection, I get it. But that&amp;rsquo;s the architecture. It&amp;rsquo;s on the pricing page. The agent works on your local files, the reasoning runs on a remote model. Both true, neither a secret.&lt;/p&gt;</description></item><item><title>What compounds</title><link>https://blog.vnykmshr.com/writing/what-compounds/</link><pubDate>Fri, 20 Mar 2026 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/what-compounds/</guid><description>&lt;p&gt;Something shifted. Not the AI thing &amp;ndash; everyone noticed that. What counts as proof.&lt;/p&gt;
&lt;p&gt;Used to be your resume, your title, the logo. Still opens doors. But the gap between &amp;ldquo;I can do X&amp;rdquo; and &amp;ldquo;here&amp;rsquo;s the commit&amp;rdquo; got wide enough that both sides feel it. A merged PR has a commit hash. A CVE has a number. A library someone depends on has a git log. Credentials got easier to claim. Artifacts didn&amp;rsquo;t.&lt;/p&gt;</description></item><item><title>The invitation</title><link>https://blog.vnykmshr.com/writing/the-invitation/</link><pubDate>Wed, 18 Mar 2026 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/the-invitation/</guid><description>&lt;p&gt;First PR to an open source project, you&amp;rsquo;re proving you can read. That you studied the codebase, matched the style, understood why things are the way they are before suggesting they should be different. Most people skip this. Most PRs show it.&lt;/p&gt;
&lt;p&gt;The second and third, you&amp;rsquo;re proving you&amp;rsquo;ll stay. Maintainers have seen hundreds of drive-by contributions. One PR, gone forever. The ones who come back are rare enough to notice.&lt;/p&gt;</description></item><item><title>The loop</title><link>https://blog.vnykmshr.com/writing/the-loop/</link><pubDate>Sat, 14 Mar 2026 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/the-loop/</guid><description>&lt;p&gt;A handful of Go libraries on GitHub. MIT licensed, anyone can use them for anything, that was always the deal.&lt;/p&gt;
&lt;p&gt;But the deal isn&amp;rsquo;t about the license. It&amp;rsquo;s about the loop.&lt;/p&gt;
&lt;p&gt;Someone uses your thing, hits an edge case, opens an issue. Sometimes they send a fix. You review it, learn how people actually use what you built, catch a pattern you missed. That back and forth is the whole point. Code just sits there without it.&lt;/p&gt;</description></item><item><title>Vortex architecture</title><link>https://blog.vnykmshr.com/writing/vortex-architecture/</link><pubDate>Wed, 11 Mar 2026 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/vortex-architecture/</guid><description>&lt;p&gt;Tesla had this thing about 3, 6, and 9. &amp;ldquo;If you only knew the magnificence of the 3, 6, and 9, then you would have a key to the universe.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Take any doubling sequence. 1, 2, 4, 8, 16, 32, 64. Reduce each to its digital root &amp;ndash; keep adding digits until you get one number. You get 1, 2, 4, 8, 7, 5. Then it repeats. Forever. Six numbers doing all the motion, cycling endlessly.&lt;/p&gt;</description></item><item><title>The detection trap</title><link>https://blog.vnykmshr.com/writing/detection-trap/</link><pubDate>Sun, 08 Mar 2026 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/detection-trap/</guid><description>&lt;p&gt;Read &lt;a href="https://www.techdirt.com/2026/03/06/were-training-students-to-write-worse-to-prove-theyre-not-robots-and-its-pushing-them-to-use-more-ai/"&gt;something recently&lt;/a&gt; about students deliberately making their writing &amp;ldquo;imperfect&amp;rdquo; so AI detectors don&amp;rsquo;t flag it. Removing polish, flattening style, adding imperfections on purpose. Their work got good enough to look suspicious.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re doing the same thing with code reviews.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been on both sides of this. Written a clean abstraction, consistent naming, proper error boundaries, and watched someone in review go &amp;ldquo;this looks generated.&amp;rdquo; Years of caring about consistency and now consistency is the tell.&lt;/p&gt;</description></item><item><title>Garbage context in, garbage code out</title><link>https://blog.vnykmshr.com/writing/garbage-context-garbage-code/</link><pubDate>Sat, 07 Mar 2026 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/garbage-context-garbage-code/</guid><description>&lt;p&gt;LLMs are exactly as good as what you feed them.&lt;/p&gt;
&lt;p&gt;Experienced engineers feel like the gap between them and everyone else just got smaller. Someone with six months of prompting can ship something that looks like what took years to learn how to build. That&amp;rsquo;s the wrong read.&lt;/p&gt;
&lt;p&gt;The output looks the same if you don&amp;rsquo;t look closely. The architecture doesn&amp;rsquo;t. The failure modes don&amp;rsquo;t. When traffic spikes and that generated code hits a path nobody thought about, the years show up.&lt;/p&gt;</description></item><item><title>The personal agent trap</title><link>https://blog.vnykmshr.com/writing/personal-agent-trap/</link><pubDate>Sat, 28 Feb 2026 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/personal-agent-trap/</guid><description>&lt;p&gt;Spent a week going through the personal agent ecosystem &amp;ndash; OpenClaw, ZeroClaw, PicoClaw, the whole *Claw family. Channel testing, security audit, the whole thing.&lt;/p&gt;
&lt;p&gt;If you want a personal assistant that messages you reminders, triages your inbox, schedules things, posts updates &amp;ndash; these frameworks are actually good at that. OpenClaw connects to 50+ channels out of the box, the setup is real, it works. For that, a $7 VPS and an afternoon gets you something useful.&lt;/p&gt;</description></item><item><title>Reading code</title><link>https://blog.vnykmshr.com/writing/reading-code/</link><pubDate>Fri, 27 Feb 2026 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/reading-code/</guid><description>&lt;p&gt;Scanners find what&amp;rsquo;s syntactically wrong. The interesting issues live in assumptions &amp;ndash; and assumptions don&amp;rsquo;t have signatures.&lt;/p&gt;
&lt;p&gt;Not scanning, not fuzzing. Just reading code the way you&amp;rsquo;d read it if you were about to own it in production. Entry points, data flows, where input meets trust.&lt;/p&gt;
&lt;p&gt;Missing headers, outdated dependencies &amp;ndash; that&amp;rsquo;s the baseline, scanners handle it fine. The interesting issues live a layer deeper. A path that&amp;rsquo;s protected in one subsystem but wide open in another. A parse-time operation that nobody thought to bound. Code that was correct when it was written but the system grew around it.&lt;/p&gt;</description></item><item><title>Repeat yourself</title><link>https://blog.vnykmshr.com/writing/repeat-yourself/</link><pubDate>Wed, 18 Feb 2026 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/repeat-yourself/</guid><description>&lt;p&gt;If you repeat your prompt, the model gives you a better answer. Not a smarter model, not a bigger context window, not chain of thought &amp;ndash; you say the same thing twice and it works better. &lt;a href="https://arxiv.org/abs/2512.14982"&gt;Google researchers tested this&lt;/a&gt; across Gemini, GPT, Claude, DeepSeek &amp;ndash; 47 wins out of 70 benchmarks, zero losses.&lt;/p&gt;
&lt;p&gt;In a transformer, token 1 can&amp;rsquo;t see token 50. Causal masking &amp;ndash; each token only attends to what came before it. The first words of your prompt are always processed with the least context. They&amp;rsquo;re flying blind. When you repeat the prompt, the second copy&amp;rsquo;s early tokens can attend to the entire first copy. You&amp;rsquo;re giving the beginning of your question the context it never had.&lt;/p&gt;</description></item><item><title>Hello world, printf</title><link>https://blog.vnykmshr.com/writing/hello-world-printf/</link><pubDate>Mon, 16 Feb 2026 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/hello-world-printf/</guid><description>&lt;p&gt;Printf wasn&amp;rsquo;t always there. Before it, you wrote to stdout directly. Before stdout, a syscall. Before the syscall, you poked bytes into a memory-mapped display buffer. Before the memory map, you flipped switches on a front panel and watched lights blink back.&lt;/p&gt;
&lt;p&gt;Every layer down, someone built something so the next person wouldn&amp;rsquo;t have to. That&amp;rsquo;s the whole field. Languages we didn&amp;rsquo;t design, compilers we didn&amp;rsquo;t write, protocols we didn&amp;rsquo;t invent. We&amp;rsquo;ve always stood on a stack of other people&amp;rsquo;s work and called the output ours. Nobody ever had a problem with that.&lt;/p&gt;</description></item><item><title>The senior who stopped coding</title><link>https://blog.vnykmshr.com/writing/senior-who-stopped-coding/</link><pubDate>Tue, 20 Jan 2026 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/senior-who-stopped-coding/</guid><description>&lt;p&gt;The terminal closes slowly.&lt;/p&gt;
&lt;p&gt;First it&amp;rsquo;s one meeting. Then a few more. Then you&amp;rsquo;re &amp;ldquo;senior&amp;rdquo; and your calendar is the job. Code reviews replace coding. Strategy replaces shipping. You advise. You guide. You no longer build. Seen this happen. Almost happened to me.&lt;/p&gt;
&lt;p&gt;The problem is not the meetings. The problem is losing touch with the trade. Architecture diagrams don&amp;rsquo;t show you the queries that fan out under load. Sprint planning doesn&amp;rsquo;t show you the retry logic that fails silently. You can&amp;rsquo;t review what you can&amp;rsquo;t recognize.&lt;/p&gt;</description></item><item><title>The happy path</title><link>https://blog.vnykmshr.com/writing/plumbing-and-abstractions/</link><pubDate>Fri, 16 Jan 2026 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/plumbing-and-abstractions/</guid><description>&lt;p&gt;The diagrams always show the happy path. Request goes in, response comes out, maybe a queue in between. Three boxes and two arrows. The failure modes live in the whitespace.&lt;/p&gt;
&lt;p&gt;Distributed systems work at demo time. In production, the third service fails after the first two succeeded: the debit went through, the credit went through, the write to the audit log dropped on a socket timeout, and both sides of the transfer had moved without anything recording &lt;em&gt;that&lt;/em&gt; they had moved. The same row was about to be replayed the next morning. Someone noticed the mismatch six weeks later, at the end of the month, in a spreadsheet one of the ops people was maintaining by hand.&lt;/p&gt;</description></item><item><title>Coding with LLMs</title><link>https://blog.vnykmshr.com/writing/coding-with-llms/</link><pubDate>Tue, 13 Jan 2026 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/coding-with-llms/</guid><description>&lt;p&gt;The tool changed. The craft did not.&lt;/p&gt;
&lt;p&gt;Six months with AI assistants.&lt;/p&gt;
&lt;p&gt;They write code faster than I read it. That is the problem.&lt;/p&gt;
&lt;p&gt;Fast at CRUD. Slow at concurrency. Good at common patterns. Bad at your specific constraints.&lt;/p&gt;
&lt;p&gt;The bugs are quieter now. No syntax errors. No obvious mistakes. Just wrong assumptions buried in correct-looking code.&lt;/p&gt;
&lt;p&gt;I review more carefully than before. Code I did not write but will debug in production.&lt;/p&gt;</description></item><item><title>autobreaker: adaptive circuit breaking</title><link>https://blog.vnykmshr.com/writing/autobreaker/</link><pubDate>Sat, 15 Nov 2025 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/autobreaker/</guid><description>&lt;p&gt;The &lt;a href="https://blog.vnykmshr.com/writing/circuit-breaking-go/"&gt;circuit breaker post&lt;/a&gt; from last year used a common trigger: trip after N consecutive failures. This works when traffic is predictable. It falls apart when it&amp;rsquo;s not.&lt;/p&gt;
&lt;p&gt;At 10,000 requests per second, 10 failures is noise &amp;ndash; a 0.1% error rate. A static threshold trips the circuit on what&amp;rsquo;s essentially a healthy service. At 10 requests per second, 10 failures is total collapse &amp;ndash; 100% error rate over one interval. The same threshold that false-positives under high traffic is too slow to protect under low traffic.&lt;/p&gt;</description></item><item><title>Replacing OCR with Gemini</title><link>https://blog.vnykmshr.com/writing/gemini-ocr/</link><pubDate>Thu, 10 Jul 2025 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/gemini-ocr/</guid><description>&lt;p&gt;The previous post covered an &lt;a href="https://blog.vnykmshr.com/writing/fixing-ocr-addresses/"&gt;address sanitizer&lt;/a&gt; that fixes mangled OCR output using multi-strategy matching. It works, but it&amp;rsquo;s treating a symptom. A smarter OCR step would make most of it unnecessary.&lt;/p&gt;
&lt;p&gt;Traditional OCR extracts characters, then downstream code figures out what they mean. A separate pipeline handles structure, validation, error correction. The address sanitizer is part of that pipeline. It exists because the OCR engine doesn&amp;rsquo;t understand what it&amp;rsquo;s reading.&lt;/p&gt;</description></item><item><title>Fixing OCR addresses</title><link>https://blog.vnykmshr.com/writing/fixing-ocr-addresses/</link><pubDate>Sat, 05 Jul 2025 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/fixing-ocr-addresses/</guid><description>&lt;p&gt;OCR on government documents works well until you look at the address fields.&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;State: &amp;#34;DKI JAKRTA&amp;#34;
City: &amp;#34;JAKRTA PUSAT&amp;#34;
District: &amp;#34;MENTNG&amp;#34;
Village: &amp;#34;MENTENG&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Dropped vowels, character substitutions, truncated names. Indonesian place names get mangled in predictable ways &amp;ndash; &amp;lsquo;A&amp;rsquo; becomes &amp;lsquo;R&amp;rsquo;, characters vanish mid-word. The OCR engine reads the image fine. It just can&amp;rsquo;t spell.&lt;/p&gt;
&lt;p&gt;The problem: take these broken strings and map them back to real administrative divisions. Province, city, district, village &amp;ndash; the hierarchy matters, and every level needs to resolve correctly.&lt;/p&gt;</description></item><item><title>McKenna and the architecture of consciousness</title><link>https://blog.vnykmshr.com/writing/mckenna-systems/</link><pubDate>Mon, 23 Jun 2025 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/mckenna-systems/</guid><description>&lt;p&gt;Terence McKenna never wrote a line of code, but he thought like a systems architect. His subject was consciousness, culture, and language. His method was the same recursive decomposition that engineers use to understand complex systems: find the abstraction layers, trace the dependencies, question the defaults.&lt;/p&gt;
&lt;h2 id="language-as-protocol"&gt;Language as protocol&lt;/h2&gt;
&lt;p&gt;McKenna&amp;rsquo;s core claim: the world is made of language. Not literally &amp;ndash; but in the way that a data model shapes everything built on top of it. Choose the wrong abstraction early, and you spend years working around its constraints. The same principle applies to the linguistic and conceptual frameworks we inherit.&lt;/p&gt;</description></item><item><title>Primary PII</title><link>https://blog.vnykmshr.com/writing/primary-pii/</link><pubDate>Tue, 05 Nov 2024 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/primary-pii/</guid><description>&lt;p&gt;A regulation arrives. Or an auditor. Or a new market with stricter rules. PII is a thing the application was always sloppy about, and now it is a thing the application has to be careful with. This is how PII externalization begins: as someone else&amp;rsquo;s deadline, landing on the engineering team as an initiative.&lt;/p&gt;
&lt;p&gt;The work looks like encryption at first. It is not.&lt;/p&gt;
&lt;h2 id="identify"&gt;Identify&lt;/h2&gt;
&lt;p&gt;The first question is not how to encrypt. The first question is what to encrypt.&lt;/p&gt;</description></item><item><title>Circuit breaking in Go</title><link>https://blog.vnykmshr.com/writing/circuit-breaking-go/</link><pubDate>Sat, 28 Sep 2024 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/circuit-breaking-go/</guid><description>&lt;p&gt;A service calls a dependency. The dependency is slow or down. The service waits, ties up a goroutine, maybe a connection. Multiply that by every request in flight, and the caller is now as broken as the dependency it called.&lt;/p&gt;
&lt;p&gt;Circuit breaking stops this. Instead of waiting on something that&amp;rsquo;s failing, stop calling it. Let it recover. Try again later.&lt;/p&gt;
&lt;h2 id="three-states"&gt;Three states&lt;/h2&gt;
&lt;p&gt;A circuit breaker wraps external calls and tracks their outcomes.&lt;/p&gt;</description></item><item><title>Scout, plan, wait</title><link>https://blog.vnykmshr.com/writing/scout-plan-wait/</link><pubDate>Tue, 20 Aug 2024 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/scout-plan-wait/</guid><description>&lt;p&gt;The legacy codebase still runs the business. It is not small. It is six vertical functions deployed as separate services, sharing a data layer and a code tree, so &amp;ldquo;service&amp;rdquo; is a deployment unit here, not a boundary. It reads like a place rather than an architecture &amp;ndash; rooms we know the shortcuts of, walls not quite where a greenfield build would put them. It has been running for years. It works.&lt;/p&gt;</description></item><item><title>Redis caching patterns</title><link>https://blog.vnykmshr.com/writing/redis-caching-patterns/</link><pubDate>Thu, 20 Jun 2024 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/redis-caching-patterns/</guid><description>&lt;p&gt;Put Redis in front of a database and reads get fast. The cost is a cache layer that&amp;rsquo;s now load-bearing, and a set of failure modes that come with that.&lt;/p&gt;
&lt;p&gt;Three write patterns, three hard problems. The patterns determine consistency. The problems determine whether your cache layer is a net positive or a source of outages.&lt;/p&gt;
&lt;h2 id="write-patterns"&gt;Write patterns&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Cache-aside&lt;/strong&gt; (lazy loading). The application checks cache on read. On miss, it reads from the database and populates cache. Writes go directly to the database; cache entries are either invalidated or left to expire.&lt;/p&gt;</description></item><item><title>The first five minutes</title><link>https://blog.vnykmshr.com/writing/system-design-interviews/</link><pubDate>Mon, 30 Jan 2023 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/system-design-interviews/</guid><description>&lt;p&gt;The prompt lands. A senior engineer, capable and prepared, starts drawing boxes. Load balancer, service tier, message broker, cache. Three minutes in, they are talking about shard counts.&lt;/p&gt;
&lt;p&gt;The design is not wrong. It is not a design either. It is a collection of patterns assembled against a problem that has not been defined.&lt;/p&gt;
&lt;p&gt;A system design prompt is deliberately loose. &amp;ldquo;Design a notification system for ten million users&amp;rdquo; has four different answers depending on what kind of notifications and which ones can be lost. &amp;ldquo;Design a URL shortener&amp;rdquo; has one shape at a thousand users and a different one at a billion. The looseness is the test. A person doing this work in production would not touch a whiteboard until the brief was tight enough to design against. The candidate who treats the interview as a different kind of activity from the work has missed what the interview is for.&lt;/p&gt;</description></item><item><title>Building from zero, twice</title><link>https://blog.vnykmshr.com/writing/building-from-zero-twice/</link><pubDate>Mon, 15 Aug 2022 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/building-from-zero-twice/</guid><description>&lt;p&gt;I&amp;rsquo;ve built remote engineering centers from scratch twice now. The first one grew to over two hundred people over several years. The second is eight, and it&amp;rsquo;s not clear yet whether it&amp;rsquo;ll get bigger. Different companies, different products, different scales. The process is more similar than I expected.&lt;/p&gt;
&lt;h2 id="starting-from-one"&gt;Starting from one&lt;/h2&gt;
&lt;p&gt;The first time, I helped set up the remote center while still at headquarters, then moved there as it grew. The second time, I was the first hire. Both times, the main engineering team was somewhere else &amp;ndash; a team that had been working together for years.&lt;/p&gt;</description></item><item><title>Evidence</title><link>https://blog.vnykmshr.com/writing/evidence/</link><pubDate>Fri, 10 Sep 2021 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/evidence/</guid><description>&lt;p&gt;A product manager pulls up a dashboard mid-meeting and the debate ends.&lt;/p&gt;
&lt;p&gt;We had been talking for twenty minutes about whether a new feature should be prioritized. Opinions on both sides. The PM clicks, runs a query, flips the panel to a view they saved last month. The graph shows the answer. We move on.&lt;/p&gt;
&lt;p&gt;This is not an unusual meeting. By 2021, it is every meeting.&lt;/p&gt;
&lt;h2 id="what-engineers-always-had"&gt;What engineers always had&lt;/h2&gt;
&lt;p&gt;The component-level observability has been in place for years. SLOs per service. Latency histograms. Request traces that let you follow a single call across twelve systems. Error rate charts with thresholds. Per-service dashboards bookmarked by the team that owns each service.&lt;/p&gt;</description></item><item><title>PostgreSQL HA</title><link>https://blog.vnykmshr.com/writing/postgres-ha/</link><pubDate>Mon, 15 Mar 2021 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/postgres-ha/</guid><description>&lt;p&gt;PostgreSQL&amp;rsquo;s streaming replication is straightforward to set up. The documentation is clear, the configuration is well-understood, and base backups with &lt;code&gt;pg_basebackup&lt;/code&gt; work reliably.&lt;/p&gt;
&lt;p&gt;The operational problems are the hard part. They show up when the primary goes down and the automated failover does the wrong thing. Or when you promote a replica that&amp;rsquo;s silently been two hours behind. Or when you discover that backups you&amp;rsquo;ve been taking for months don&amp;rsquo;t actually restore.&lt;/p&gt;</description></item><item><title>The crossover</title><link>https://blog.vnykmshr.com/writing/the-crossover/</link><pubDate>Wed, 23 Sep 2020 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/the-crossover/</guid><description>&lt;p&gt;The second cloud migration lands on the roadmap in a staff meeting. On paper it looks like the first one. A target provider, a rough timeline, a named lead. I read the slide and expect the same work.&lt;/p&gt;
&lt;p&gt;It is not the same work.&lt;/p&gt;
&lt;h2 id="the-first-time"&gt;The first time&lt;/h2&gt;
&lt;p&gt;The first migration was easy to agree to. Our local datacenter had frequent outages and every one of them carried a physical tax &amp;ndash; somebody drove over, pulled a cable, power-cycled a router. The DC had a support team, but the parts that mattered to us were ours to maintain. Cloud was the way forward and every one of us knew it. The decision did not take a meeting.&lt;/p&gt;</description></item><item><title>Prescaling for a known spike</title><link>https://blog.vnykmshr.com/writing/prescaling-for-a-known-spike/</link><pubDate>Fri, 15 Mar 2019 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/prescaling-for-a-known-spike/</guid><description>&lt;p&gt;Our biggest sale event of the year is on the calendar. The date is fixed, the hour is fixed, and when it starts, traffic hits a multiple of normal within minutes. The engineering challenge isn&amp;rsquo;t handling surprise. It&amp;rsquo;s handling certainty at a scale we&amp;rsquo;ve never seen before.&lt;/p&gt;
&lt;p&gt;We prepare for months. Six months out, teams start thinking about what their services need. Backend teams work with SRE and infra to define prescale configurations and autoscale rules. Terraform handles the provisioning. Every service team shares their estimates with infra, and the configurations get codified.&lt;/p&gt;</description></item><item><title>Consul in practice</title><link>https://blog.vnykmshr.com/writing/consul-in-practice/</link><pubDate>Mon, 10 Sep 2018 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/consul-in-practice/</guid><description>&lt;p&gt;The microservice count is growing fast. The monolith is mostly gone and what replaced it is dozens of services across datacenters. We don&amp;rsquo;t have a uniform naming convention. Finding a service means knowing which team owns it, which cloud it&amp;rsquo;s on, and what they called it. That&amp;rsquo;s not scalable.&lt;/p&gt;
&lt;p&gt;Consul fixed the naming problem first.&lt;/p&gt;
&lt;h2 id="service-discovery"&gt;Service discovery&lt;/h2&gt;
&lt;p&gt;Every service registers with Consul. The DNS interface gives us a consistent way to find anything:&lt;/p&gt;</description></item><item><title>The compiler as first reviewer</title><link>https://blog.vnykmshr.com/writing/the-compiler-as-first-reviewer/</link><pubDate>Wed, 18 Jul 2018 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/the-compiler-as-first-reviewer/</guid><description>&lt;p&gt;When I joined, a few of us who had recently come in would skip the daily standups. Clock in, clock out, heads down. The rest of the team wasn&amp;rsquo;t sure what we were working on. This went on for a while. We were reading the existing codebase and quietly rewriting pieces in Go &amp;ndash; proving it could handle production before anyone had to commit to it.&lt;/p&gt;
&lt;p&gt;That changed when Go got formally introduced. The direction came from above &amp;ndash; Go was the language for the new services. Now it wasn&amp;rsquo;t a side experiment, it was the stack. And the PRs were coming in faster than anyone could review them all.&lt;/p&gt;</description></item><item><title>The week pgbouncer stopped being news</title><link>https://blog.vnykmshr.com/writing/pgbouncer-stopped-being-news/</link><pubDate>Thu, 12 Jul 2018 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/pgbouncer-stopped-being-news/</guid><description>&lt;p&gt;The connection count climbs faster than our instance classes can keep up. Ops is hot. Every few weeks the same thread resurfaces: we need a pool in front of Postgres before the next scale event.&lt;/p&gt;
&lt;p&gt;We move on pgbouncer.&lt;/p&gt;
&lt;h2 id="the-choice"&gt;The choice&lt;/h2&gt;
&lt;p&gt;Two modes on the table. Session pooling hands a connection to a client and gives it back when the client disconnects. Transaction pooling hands one out per transaction. Transaction is tighter &amp;ndash; the pool stretches further, the math gets better &amp;ndash; but the client loses everything a session holds. Server-side prepared statements. Advisory locks. Temp tables. &lt;code&gt;SET&lt;/code&gt; commands that expect to persist.&lt;/p&gt;</description></item><item><title>The GraphQL buffer</title><link>https://blog.vnykmshr.com/writing/the-graphql-buffer/</link><pubDate>Fri, 20 Apr 2018 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/the-graphql-buffer/</guid><description>&lt;p&gt;The GraphQL gateway started as a practical problem. We had mobile apps, web clients, and a growing number of backend services. Every client talked to every backend directly. When a new backend came up or an old one changed its API, every client needed updating. The gateway was supposed to fix that &amp;ndash; one schema, one endpoint, clients talk to GraphQL, GraphQL talks to backends.&lt;/p&gt;
&lt;p&gt;We built it in Go, starting from a fork of &lt;code&gt;graphql-go&lt;/code&gt;. The fork grew over time &amp;ndash; custom resolvers, caching layers, request batching, things we needed that the upstream didn&amp;rsquo;t have. We&amp;rsquo;d sync the fork every few months, but our changes kept growing. Five of us on the team, and most of the early days went into getting other teams to migrate their APIs onto the gateway. We built the base, got teams to add and own their own modules, then moved into a gatekeeping role &amp;ndash; reviewing what went in, making sure the schema stayed coherent.&lt;/p&gt;</description></item><item><title>The war room</title><link>https://blog.vnykmshr.com/writing/the-war-room/</link><pubDate>Sat, 10 Feb 2018 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/the-war-room/</guid><description>&lt;p&gt;By the morning of the event, there is nothing left to prepare. The capacity plan is done. The prescale is loaded. Infra has been briefed, service owners have been briefed, the runbook has been circulated.&lt;/p&gt;
&lt;p&gt;The traffic comes in multi-x what we had planned for.&lt;/p&gt;
&lt;p&gt;Nothing about the plan anticipated this. The numbers we used were the best numbers we had. The headroom we carved was the headroom we could afford. Neither was enough.&lt;/p&gt;</description></item><item><title>The same tree, twice</title><link>https://blog.vnykmshr.com/writing/the-same-tree-twice/</link><pubDate>Fri, 22 Sep 2017 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/the-same-tree-twice/</guid><description>&lt;p&gt;I am building the promocode engine again.&lt;/p&gt;
&lt;p&gt;This is the second time. The first was in Node.js, at a previous company, on top of a small library called &lt;code&gt;business-rules&lt;/code&gt;. The engine worked. I thought the shape was brilliant.&lt;/p&gt;
&lt;p&gt;I am building it again in Go, from scratch, and the shape is exactly the same.&lt;/p&gt;
&lt;h2 id="the-first-time"&gt;The first time&lt;/h2&gt;
&lt;p&gt;The first promocode engine was an AND/OR decision tree. A rule had conditions in groups, groups nested inside groups, leaves that compared a fact to a value. A rule was &lt;em&gt;data&lt;/em&gt; in the database &amp;ndash; a flattened tree stored across MySQL rows &amp;ndash; but it evaluated against facts that had to be computed live.&lt;/p&gt;</description></item><item><title>Designing a wallet</title><link>https://blog.vnykmshr.com/writing/designing-a-wallet/</link><pubDate>Sun, 20 Aug 2017 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/designing-a-wallet/</guid><description>&lt;p&gt;I&amp;rsquo;m building a wallet service in Go. Users add money from their bank, the balance sits in the wallet, and they spend it on the platform. Small payments &amp;ndash; the kind where going through a full bank authentication flow every time is more friction than the transaction is worth.&lt;/p&gt;
&lt;p&gt;The pitch is simple: top up once, spend without thinking. No OTP for every payment. No redirect to the bank&amp;rsquo;s page. One click, done.&lt;/p&gt;</description></item><item><title>Hazard lights</title><link>https://blog.vnykmshr.com/writing/hazard-lights/</link><pubDate>Sat, 10 Jun 2017 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/hazard-lights/</guid><description>&lt;p&gt;There are about fifteen of us in the enclosure. Backend engineers, SRE, devops, infra &amp;ndash; handpicked from across the floor. The rest of the team, about a hundred people, sit outside. They call us the fishes in the aquarium.&lt;/p&gt;
&lt;p&gt;The aquarium has hazard lights. Physical ones &amp;ndash; wired to fire on any 5xx in the system. When something breaks in production, the room goes red.&lt;/p&gt;
&lt;p&gt;It sounds like a gimmick. It isn&amp;rsquo;t.&lt;/p&gt;</description></item><item><title>Nginx load balancing decisions</title><link>https://blog.vnykmshr.com/writing/nginx-load-balancing/</link><pubDate>Thu, 18 May 2017 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/nginx-load-balancing/</guid><description>&lt;p&gt;Nginx as a reverse proxy and load balancer is well-documented. The configuration syntax is not the hard part. The decisions are.&lt;/p&gt;
&lt;h2 id="algorithm-selection"&gt;Algorithm selection&lt;/h2&gt;
&lt;p&gt;Three algorithms cover most workloads.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Round-robin&lt;/strong&gt; (the default). Requests cycle through backends sequentially. Weights let you bias toward higher-capacity servers. Simple, predictable, works well when request processing times are uniform.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;upstream&lt;/span&gt; &lt;span class="s"&gt;api&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="n"&gt;api-01&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;8080&lt;/span&gt; &lt;span class="s"&gt;weight=3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="n"&gt;api-02&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;8080&lt;/span&gt; &lt;span class="s"&gt;weight=2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="n"&gt;api-03&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;8080&lt;/span&gt; &lt;span class="s"&gt;weight=1&lt;/span&gt; &lt;span class="s"&gt;backup&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;keepalive&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;backup&lt;/code&gt; directive keeps a server in reserve &amp;ndash; it only receives traffic when all non-backup servers are down. Useful for a smaller instance that can keep the service alive during a partial outage but shouldn&amp;rsquo;t take production load normally.&lt;/p&gt;</description></item><item><title>The first service</title><link>https://blog.vnykmshr.com/writing/carving-the-first-service/</link><pubDate>Wed, 15 Mar 2017 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/carving-the-first-service/</guid><description>&lt;p&gt;The monolith is Perl. No framework I can identify &amp;ndash; just a large codebase that&amp;rsquo;s been growing for years. One Perl line does an alarming amount. I&amp;rsquo;m not a Perl developer and had to read through the code several times before I was confident I understood what it was doing.&lt;/p&gt;
&lt;p&gt;Production goes down for days sometimes. When it does, the team spends hours tracing through the code to figure out what broke. That&amp;rsquo;s the context. The system works, mostly. When it doesn&amp;rsquo;t, nobody quite knows why.&lt;/p&gt;</description></item><item><title>Switching to Go</title><link>https://blog.vnykmshr.com/writing/switching-to-go/</link><pubDate>Wed, 07 Dec 2016 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/switching-to-go/</guid><description>&lt;p&gt;I&amp;rsquo;d been building production services with Node.js since 2013. Custom blog engines, API services, real-time backends. The ecosystem was rich, iteration was fast, and for moderate-load services, it worked well.&lt;/p&gt;
&lt;p&gt;The limits showed up at scale. A latency-sensitive payment processing API exposed three problems that were manageable at lower traffic but compounded at higher volumes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GC pauses.&lt;/strong&gt; Node&amp;rsquo;s garbage collector introduced latency spikes that were unpredictable and hard to tune. For payment processing, where consistent response times matter more than peak throughput, this was the biggest concern. A p99 spike during a GC pause means a user staring at a spinner while their payment hangs.&lt;/p&gt;</description></item><item><title>Building with one other person</title><link>https://blog.vnykmshr.com/writing/building-with-one-other-person/</link><pubDate>Wed, 25 Mar 2015 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/building-with-one-other-person/</guid><description>&lt;p&gt;Our codebase has about a hundred JavaScript files and 96 Jade templates. Around 7,500 lines of server-side code. 352 commits &amp;ndash; 228 mine, 123 Jyoti&amp;rsquo;s. The readme is two lines. The todo list has two items &amp;ndash; one about image upload paths, one about a &lt;code&gt;bodyParser()&lt;/code&gt; deprecation we never got around to fixing.&lt;/p&gt;
&lt;p&gt;This is what a web application looks like when two people build the whole thing.&lt;/p&gt;
&lt;h2 id="the-split"&gt;The split&lt;/h2&gt;
&lt;p&gt;I own backend, database, and the graph model. Jyoti owns frontend &amp;ndash; views, templates, client-side interactions. There&amp;rsquo;s overlap in the middle. Routes are mine. The Jade templates that render those routes are hers. The &lt;code&gt;res.locals&lt;/code&gt; object is our contract &amp;ndash; I populate it with data from the middleware chain, she reads it in the templates. We rarely step on each other&amp;rsquo;s code.&lt;/p&gt;</description></item><item><title>Graph queries for a social feed</title><link>https://blog.vnykmshr.com/writing/graph-queries-for-a-social-feed/</link><pubDate>Tue, 10 Feb 2015 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/graph-queries-for-a-social-feed/</guid><description>&lt;p&gt;We&amp;rsquo;re building a platform with a social feed. Not a timeline of photos or opinions &amp;ndash; a feed of plans. Things people intend to do. The interesting question isn&amp;rsquo;t &amp;ldquo;what did my friend share&amp;rdquo; but &amp;ldquo;what is my friend&amp;rsquo;s friend doing this Saturday, and can I join.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;That second question &amp;ndash; friend-of-friend discovery &amp;ndash; is what made me reach for a graph database.&lt;/p&gt;
&lt;h2 id="why-graph"&gt;Why graph&lt;/h2&gt;
&lt;p&gt;At first glance, a feed is just a list. The interesting part is underneath &amp;ndash; a web of relationships: who knows whom, who created what, who joined what.&lt;/p&gt;</description></item><item><title>Two-sided graph</title><link>https://blog.vnykmshr.com/writing/two-sided-graph/</link><pubDate>Sat, 15 Nov 2014 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/two-sided-graph/</guid><description>&lt;p&gt;The platform started with one side &amp;ndash; users making plans, tagging friends, joining each other&amp;rsquo;s plans. The graph model handles that well. Users connect through &lt;code&gt;KNOWS&lt;/code&gt; edges, plans connect through &lt;code&gt;CREATED&lt;/code&gt;, &lt;code&gt;JOINED&lt;/code&gt;, &lt;code&gt;TAGGED&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now we&amp;rsquo;re adding the other side. Businesses. A cafe with a slow Tuesday wants to drop a deal into the plans forming around it. A venue can offer a discount to a group that&amp;rsquo;s already half-organized. The product idea from the beginning was that both sides would share the same surface &amp;ndash; people with half-formed plans and businesses with empty hours, meeting in the same feed.&lt;/p&gt;</description></item><item><title>The middleware chain</title><link>https://blog.vnykmshr.com/writing/the-middleware-chain/</link><pubDate>Wed, 20 Aug 2014 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/the-middleware-chain/</guid><description>&lt;p&gt;I wrote about &lt;a href="https://blog.vnykmshr.com/writing/express-middleware-chains/"&gt;Express middleware chains&lt;/a&gt; last year as a pattern for keeping route handlers flat. That was a toy example. This is what it looks like in a real application.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re two people building a platform. The home page needs to show the user&amp;rsquo;s feed, friend recommendations, today&amp;rsquo;s happenings, friend request count, and unread notifications. That&amp;rsquo;s five data sources, each requiring its own database query, each needing the user to be authenticated first.&lt;/p&gt;</description></item><item><title>Git history with Gource</title><link>https://blog.vnykmshr.com/writing/git-history-with-gource/</link><pubDate>Sat, 07 Jun 2014 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/git-history-with-gource/</guid><description>&lt;p&gt;I&amp;rsquo;d been watching YouTube videos of &lt;a href="https://code.google.com/p/gource/"&gt;Gource&lt;/a&gt; visualizations &amp;ndash; &lt;a href="https://www.youtube.com/watch?v=GTMC3g2Xy8c"&gt;Git&lt;/a&gt;, &lt;a href="http://www.youtube.com/watch?v=ZX0xCWANfW4"&gt;Homebrew&lt;/a&gt;, &lt;a href="https://www.youtube.com/watch?v=vsMUTsFdzr4"&gt;Node.js&lt;/a&gt;. Each commit becomes a moment in time, files branch out like a digital organism, contributors appear and disappear. Months of development condensed into a few minutes of organic growth.&lt;/p&gt;
&lt;p&gt;I pointed it at my blog&amp;rsquo;s git repository. Here&amp;rsquo;s &lt;a href="https://www.youtube.com/watch?v=jubNs7aXEvQ"&gt;what came out&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="setup"&gt;Setup&lt;/h2&gt;
&lt;p&gt;On macOS, Homebrew handles everything:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;brew update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;brew install gource ffmpeg
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The command that generates a video from a git repo:&lt;/p&gt;</description></item><item><title>Plans, not posts</title><link>https://blog.vnykmshr.com/writing/plans-not-posts/</link><pubDate>Wed, 30 Apr 2014 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/plans-not-posts/</guid><description>&lt;p&gt;I&amp;rsquo;ve decided to try my own gig. Against the steady cheque, against the version of my life that was already working. Writing it down so I can&amp;rsquo;t pretend I didn&amp;rsquo;t mean it.&lt;/p&gt;
&lt;p&gt;There are three of us, sketching the same thing on the same whiteboard. The idea is small and specific. Most of what gets shared online is opinion, or things that already happened. We want to build something where the unit is a &lt;em&gt;plan&lt;/em&gt; &amp;ndash; this Saturday, this street, this group, this thing we&amp;rsquo;re going to do. Friends see it, say they&amp;rsquo;re in, and now it&amp;rsquo;s on a calendar as a real thing.&lt;/p&gt;</description></item><item><title>ACCEPTed. COMMITed. RESOLVEd.</title><link>https://blog.vnykmshr.com/writing/accepted-committed-resolved/</link><pubDate>Fri, 11 Apr 2014 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/accepted-committed-resolved/</guid><description>&lt;p&gt;I came across Adrian Hands&amp;rsquo; story through a blog post. I don&amp;rsquo;t remember which one. The details stayed.&lt;/p&gt;
&lt;p&gt;Adrian was a developer with ALS. By the time he wrote his last patch, his hands were gone. He built a Morse code input rig &amp;ndash; a Darci USB emulator wrapped in PVC pipe strapped to his knee, paddles attached, tapping out dots and dashes with the last muscles that still worked. That was his keyboard.&lt;/p&gt;</description></item><item><title>Go Big or Go Home</title><link>https://blog.vnykmshr.com/writing/go-big-or-go-home/</link><pubDate>Mon, 06 Jan 2014 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/go-big-or-go-home/</guid><description>&lt;p&gt;Monday morning, early. The door to the meeting room is unlocked and the room is empty. I like getting there first &amp;ndash; it lets me see the room before it fills with the people I&amp;rsquo;m about to sit across from.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a poster on the wall.&lt;/p&gt;
&lt;p&gt;GO BIG OR GO HOME.&lt;/p&gt;
&lt;p&gt;Text on a silhouette background, designed in-house by a design team that took this sort of thing seriously. Tastefully done &amp;ndash; not a free stock-photo print, not a motivational-mousepad cliché, just a line in a clean font over a darker graphic that someone actually thought about. It&amp;rsquo;s there because someone wanted you to see it.&lt;/p&gt;</description></item><item><title>Node.js on a Raspberry Pi</title><link>https://blog.vnykmshr.com/writing/nodejs-on-raspberry-pi/</link><pubDate>Sun, 05 Jan 2014 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/nodejs-on-raspberry-pi/</guid><description>&lt;p&gt;When I first heard about the Raspberry Pi, I had to get one. A $35 computer that runs real applications. In India in 2013, getting one was the hard part.&lt;/p&gt;
&lt;p&gt;Element14 showed &amp;ldquo;6 qty available.&amp;rdquo; I ordered. The status changed to &amp;ldquo;8-9 weeks lead time.&amp;rdquo; Forty-five days later, the Pi arrived. I plugged it in &amp;ndash; nothing. A blinking red light, no display. I tried reloading Raspbian, different cables, different SD cards. Nothing worked. I packed it away and forgot about it for the better part of a year.&lt;/p&gt;</description></item><item><title>11/12/13</title><link>https://blog.vnykmshr.com/writing/11-12-13/</link><pubDate>Wed, 11 Dec 2013 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/11-12-13/</guid><description>&lt;p&gt;Today is 11/12/13. The last date of the century with three consecutive numbers. It&amp;rsquo;s also my birthday.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t usually do resolutions. I don&amp;rsquo;t usually do oaths. Today I&amp;rsquo;m making an exception and taking &lt;a href="https://dionyziz.com/oath/"&gt;A Programmer&amp;rsquo;s Oath&lt;/a&gt; &amp;ndash; a Hippocratic-style pledge for people who build software, written by Dionysis Zindros.&lt;/p&gt;
&lt;p&gt;Most of it is what you&amp;rsquo;d expect: respect the people who taught you, share what you learn, don&amp;rsquo;t claim expertise you don&amp;rsquo;t have, don&amp;rsquo;t use your power for unfair profit, keep learning.&lt;/p&gt;</description></item><item><title>Thank You, Sachin</title><link>https://blog.vnykmshr.com/writing/thank-you-sachin/</link><pubDate>Mon, 18 Nov 2013 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/thank-you-sachin/</guid><description>&lt;p&gt;Two days after the speech. I can&amp;rsquo;t stop thinking about the list.&lt;/p&gt;
&lt;p&gt;So I went looking for the numbers. Andy Zaltzman at ESPNcricinfo had done a stat dive the week before &amp;ndash; he had them all.&lt;/p&gt;
&lt;p&gt;676,693 deliveries faced, across 663 international matches. 41,113 minutes at the crease &amp;ndash; 28 days and change. 4,062 fours. 264 sixes. 982 international cricketers played with or against, 842 of them on the other side.&lt;/p&gt;</description></item><item><title>Sachin at Wankhede</title><link>https://blog.vnykmshr.com/writing/sachin-at-wankhede/</link><pubDate>Sun, 17 Nov 2013 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/sachin-at-wankhede/</guid><description>&lt;p&gt;He carried a list.&lt;/p&gt;
&lt;p&gt;Sachin Tendulkar &amp;ndash; who remembers every innings he&amp;rsquo;s played, who can tell you the field placement on a delivery from 1996 &amp;ndash; carried a piece of paper to his own farewell because he was afraid he&amp;rsquo;d forget someone to thank.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s the part that broke me.&lt;/p&gt;
&lt;p&gt;The speech itself was what you&amp;rsquo;d expect and nothing like what you&amp;rsquo;d expect. He started with his father, who told an eleven-year-old to chase his dreams without shortcuts. He thanked his mother for every prayer. He thanked Achrekar sir, his childhood coach &amp;ndash; the man who never once told him &amp;ldquo;well played.&amp;rdquo; Not once. Because he was afraid the boy would stop improving.&lt;/p&gt;</description></item><item><title>Express middleware chains</title><link>https://blog.vnykmshr.com/writing/express-middleware-chains/</link><pubDate>Tue, 15 Oct 2013 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/express-middleware-chains/</guid><description>&lt;p&gt;A typical Express route handler fetches some data and renders a template:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/book/:book&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;book&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;book&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;book&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;book&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;book&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;book&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;author&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Two async calls, three levels of nesting. Add authentication, input validation, permission checks &amp;ndash; and you&amp;rsquo;re five levels deep in a single route definition. The &lt;a href="https://blog.vnykmshr.com/writing/nodejs-style-guide/#callback-alignment"&gt;callback alignment pattern&lt;/a&gt; helps with formatting, but the nesting is structural. The route handler is doing too many things.&lt;/p&gt;
&lt;h2 id="middleware-as-steps"&gt;Middleware as steps&lt;/h2&gt;
&lt;p&gt;Express lets you pass multiple handler functions to a route. Each one gets &lt;code&gt;req&lt;/code&gt;, &lt;code&gt;res&lt;/code&gt;, and &lt;code&gt;next&lt;/code&gt;. Do your work, attach results to &lt;code&gt;req&lt;/code&gt;, call &lt;code&gt;next()&lt;/code&gt; to pass control to the next handler. Bail out early if something fails.&lt;/p&gt;</description></item><item><title>On Phabricator</title><link>https://blog.vnykmshr.com/writing/on-phabricator/</link><pubDate>Sun, 01 Sep 2013 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/on-phabricator/</guid><description>&lt;p&gt;I&amp;rsquo;ve been using Phabricator for several months now. Code review happened before Phab too &amp;ndash; we weren&amp;rsquo;t shipping blind. Years of SVN, then Mercurial hosted on Unfuddle for a while. Reviews ran through email threads, commit comments, someone walking over to your desk. It worked. But the review was always a side channel. The diff wasn&amp;rsquo;t a first-class thing. You&amp;rsquo;d commit, someone would look at it, feedback would come through whatever medium was handy, you&amp;rsquo;d iterate, it would go in. Review happened &lt;em&gt;around&lt;/em&gt; the code, not &lt;em&gt;through&lt;/em&gt; a tool built for it.&lt;/p&gt;</description></item><item><title>Zero-downtime deploys</title><link>https://blog.vnykmshr.com/writing/zero-downtime-deploys/</link><pubDate>Mon, 15 Jul 2013 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/zero-downtime-deploys/</guid><description>&lt;p&gt;I deploy everything from the terminal. No web interface, no CI service, no dashboard with green buttons. Just &lt;code&gt;deploy production&lt;/code&gt; from my laptop, and the code goes live.&lt;/p&gt;
&lt;p&gt;The setup is two pieces. A bash script that handles the remote work &amp;ndash; SSH in, pull the latest code, run hooks. And a Node.js process that watches a file on the server and reloads the app cluster when the file changes. Between them, they do zero-downtime deploys in under ten seconds.&lt;/p&gt;</description></item><item><title>Sorting by multiple fields</title><link>https://blog.vnykmshr.com/writing/sorting-by-multiple-fields/</link><pubDate>Fri, 28 Jun 2013 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/sorting-by-multiple-fields/</guid><description>&lt;p&gt;&lt;code&gt;Array.sort&lt;/code&gt; takes a comparator. One field is straightforward:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SCORE&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SCORE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Multiple fields gets ugly fast. Sort by score descending, then time ascending, then age ascending &amp;ndash; you end up chaining comparisons:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SCORE&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SCORE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TIME&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TIME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AGE&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AGE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This works for numbers but breaks down with strings, custom transforms, or when you want to configure the sort dynamically. I wanted something reusable.&lt;/p&gt;
&lt;p&gt;The pattern below is adapted from &lt;a href="https://stackoverflow.com/a/6913821"&gt;a Stack Overflow answer&lt;/a&gt; I kept coming back to &amp;ndash; writing it up here for my own reference.&lt;/p&gt;</description></item><item><title>Running Node.js in production</title><link>https://blog.vnykmshr.com/writing/nodejs-in-production/</link><pubDate>Wed, 29 May 2013 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/nodejs-in-production/</guid><description>&lt;p&gt;We&amp;rsquo;ve been running Node.js in production since the 0.4 days. The language is easy to get started with. Keeping it running under real traffic is a different problem.&lt;/p&gt;
&lt;h2 id="process-management"&gt;Process management&lt;/h2&gt;
&lt;p&gt;The application needs to start at boot, restart on crash, and respond to system signals. Upstart handles this on Ubuntu without additional dependencies:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;description &lt;span class="s2"&gt;&amp;#34;myserver&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;env &lt;span class="nv"&gt;APP_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/www/myserver/releases/current
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;env &lt;span class="nv"&gt;NODE_ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;env &lt;span class="nv"&gt;RUN_AS_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;www-data
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;start on &lt;span class="o"&gt;(&lt;/span&gt;net-device-up and local-filesystems and runlevel &lt;span class="o"&gt;[&lt;/span&gt;2345&lt;span class="o"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;stop on runlevel &lt;span class="o"&gt;[&lt;/span&gt;016&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;respawn
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;respawn limit &lt;span class="m"&gt;5&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pre-start script
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;test&lt;/span&gt; -x /usr/local/bin/node &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; stop&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; 0&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;test&lt;/span&gt; -e &lt;span class="nv"&gt;$APP_HOME&lt;/span&gt;/logs &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; stop&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; 0&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;end script
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;script
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; chdir &lt;span class="nv"&gt;$APP_HOME&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;exec&lt;/span&gt; /usr/local/bin/node bin/cluster app.js &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -u &lt;span class="nv"&gt;$RUN_AS_USER&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -l logs/myserver.out &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -e logs/myserver.err &amp;gt;&amp;gt; &lt;span class="nv"&gt;$APP_HOME&lt;/span&gt;/logs/upstart
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;end script
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;respawn limit 5 60&lt;/code&gt; prevents a crash loop &amp;ndash; if the process dies 5 times within 60 seconds, Upstart stops trying. The &lt;code&gt;pre-start&lt;/code&gt; script verifies that Node and the log directory exist before attempting to start.&lt;/p&gt;</description></item><item><title>MySQL on XFS</title><link>https://blog.vnykmshr.com/writing/mysql-xfs/</link><pubDate>Thu, 11 Apr 2013 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/mysql-xfs/</guid><description>&lt;p&gt;XFS handles database workloads better than ext4 &amp;ndash; better concurrent I/O, more efficient metadata operations for tables-heavy schemas, and delayed allocation that improves write throughput. The obvious approach is to change MySQL&amp;rsquo;s &lt;code&gt;datadir&lt;/code&gt; in the config. The less obvious approach is bind mounts, which keep every path where the system expects it.&lt;/p&gt;
&lt;h2 id="setup"&gt;Setup&lt;/h2&gt;
&lt;p&gt;Install XFS utilities alongside MySQL:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt-get install -y xfsprogs mysql-server
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Create the filesystem on the dedicated volume:&lt;/p&gt;</description></item><item><title>Node.js style guide</title><link>https://blog.vnykmshr.com/writing/nodejs-style-guide/</link><pubDate>Wed, 20 Feb 2013 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/nodejs-style-guide/</guid><description>&lt;p&gt;We&amp;rsquo;re a Node.js team of about 25 developers, most of them fresh out of college. Node was new to everyone. This is the style guide we put together to keep the codebase consistent.&lt;/p&gt;
&lt;p&gt;Based on &lt;a href="http://nodeguide.com/style.html"&gt;Felix&amp;rsquo;s Node.js Style Guide&lt;/a&gt; with our own picks where his guide left things open. Read Felix&amp;rsquo;s first &amp;ndash; this extends it.&lt;/p&gt;
&lt;h2 id="formatting"&gt;Formatting&lt;/h2&gt;
&lt;p&gt;2 spaces. No tabs. No discussion.&lt;/p&gt;
&lt;p&gt;Trailing whitespace: strip it. Your editor should handle this. If it doesn&amp;rsquo;t, fix your editor.&lt;/p&gt;</description></item><item><title>History as communication</title><link>https://blog.vnykmshr.com/writing/git-history-as-communication/</link><pubDate>Mon, 18 Feb 2013 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/git-history-as-communication/</guid><description>&lt;p&gt;Most teams treat git history as a side effect. You work, you commit, whatever ends up in the log is the log. Merge commits pile up. &amp;ldquo;WIP&amp;rdquo; and &amp;ldquo;fix typo&amp;rdquo; and &amp;ldquo;actually working now&amp;rdquo; sit next to meaningful changes. The history is technically accurate but communicates nothing.&lt;/p&gt;
&lt;p&gt;The alternative: treat history as something you write, not something that happens to you. The commit log is the one piece of documentation that&amp;rsquo;s always in sync with the code &amp;ndash; because it &lt;em&gt;is&lt;/em&gt; the code&amp;rsquo;s changelog. If the history is incoherent, you&amp;rsquo;ve wasted that.&lt;/p&gt;</description></item><item><title>Just Another Brick in the Wall</title><link>https://blog.vnykmshr.com/writing/just-another-brick-in-the-wall/</link><pubDate>Sat, 16 Feb 2013 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/just-another-brick-in-the-wall/</guid><description>&lt;p&gt;Tired of being another brick in the wall.
A cog. A faceless name.
No identity. No purpose. No dream.
Just output.&lt;/p&gt;
&lt;p&gt;I want to break out of this.
Soar. Discover what I&amp;rsquo;m actually worth.
But the walls are tall
and the chains are heavy
and every morning looks the same as the last one.&lt;/p&gt;
&lt;p&gt;I know I was meant for more than this.
Everyone thinks that.
Does everyone think that?&lt;/p&gt;</description></item><item><title>Starting over</title><link>https://blog.vnykmshr.com/writing/starting-over/</link><pubDate>Tue, 05 Feb 2013 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/starting-over/</guid><description>&lt;p&gt;Every now and then you want to start over. This blog was long overdue for it. Feels good to tear it down and rebuild.&lt;/p&gt;
&lt;p&gt;The old setup was WordPress on &lt;a href="http://nearlyfreespeech.net/"&gt;NearlyFreeSpeech.net&lt;/a&gt;. NFSN deserves credit &amp;ndash; their pay-as-you-go model is honest and the value is real. But WordPress started feeling like a constraint. I spend my days building custom solutions. Using a one-size-fits-all CMS for my own site felt wrong.&lt;/p&gt;
&lt;p&gt;So I built a blog engine.&lt;/p&gt;</description></item><item><title>Running two systems at once</title><link>https://blog.vnykmshr.com/writing/running-two-systems-at-once/</link><pubDate>Tue, 20 Nov 2012 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/running-two-systems-at-once/</guid><description>&lt;p&gt;We&amp;rsquo;re replacing the frontend of a running store. The old system handles everything &amp;ndash; catalog, cart, checkout, orders, fulfillment. The new one is Node.js. We can&amp;rsquo;t switch over in a weekend. The store has traffic every day, orders every hour. Nobody&amp;rsquo;s going to approve a &amp;ldquo;shut it down Friday, bring up the new one Monday&amp;rdquo; plan.&lt;/p&gt;
&lt;p&gt;So both systems run at the same time. On the same domain, behind the same nginx. Writing this down before I forget the order we did it in.&lt;/p&gt;</description></item><item><title>The dispatch desk</title><link>https://blog.vnykmshr.com/writing/the-dispatch-desk/</link><pubDate>Thu, 20 Sep 2012 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/the-dispatch-desk/</guid><description>&lt;p&gt;The warehouse is in the basement. Three-floor office, one floor down. Inventory on open metal shelves &amp;ndash; row by column, every product a known address. I&amp;rsquo;ve been to bigger warehouses. The kind where one end isn&amp;rsquo;t visible from the other and you can&amp;rsquo;t tell what&amp;rsquo;s going on at a glance. This one isn&amp;rsquo;t like that. You can stand at the door and see the whole operation.&lt;/p&gt;
&lt;p&gt;An order comes in upstairs. The printout arrives at the front desk, gets handed over as a pick list &amp;ndash; reordered so a person can fetch the items in a single sequential pass through the aisles. The basket goes through quality check, then into the black box &amp;ndash; three layers, bubble wrap, thermocol where it matters. A label with tracking comes off a printer and gets pasted on. The box moves to the dispatch desk. The courier comes by later in the day. Two or three days to the customer. Same-day if they&amp;rsquo;re close.&lt;/p&gt;</description></item><item><title>A fifty-line rate limiter</title><link>https://blog.vnykmshr.com/writing/a-fifty-line-rate-limiter/</link><pubDate>Wed, 05 Sep 2012 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/a-fifty-line-rate-limiter/</guid><description>&lt;p&gt;I needed rate limiting. Not a sophisticated system &amp;ndash; something that stops a single IP from hammering the app. The options were a Redis-backed solution or an npm module I hadn&amp;rsquo;t vetted. I wrote one instead.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;hits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;timers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;ratelimit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mins&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;max&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;max&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;mins&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mins&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;mins&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;ip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;x-real-ip&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remoteAddress&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;rl:&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;X-Ratelimit-Limit&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;X-Ratelimit-Remaining&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;max&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;retry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;_idleStart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;timers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;_idleTimeout&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;X-Ratelimit-Message&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Enhance Your Calm&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;X-Ratelimit-Retry-After&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;retry&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;s&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;420&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Rate limit exceeded. Try again in ~&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;retry&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;s.&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;hits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;timers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;timers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;hits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;timers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it. Wire it into Express as middleware:&lt;/p&gt;</description></item><item><title>Connection pooling</title><link>https://blog.vnykmshr.com/writing/connection-pooling-nodejs-mysql/</link><pubDate>Fri, 20 Jul 2012 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/connection-pooling-nodejs-mysql/</guid><description>&lt;p&gt;The &lt;code&gt;mysql&lt;/code&gt; npm module creates one connection per client instance. Every database call opens a connection, runs the query, and you&amp;rsquo;re responsible for closing it. Under concurrent requests, connections pile up. MySQL has a &lt;code&gt;max_connections&lt;/code&gt; setting &amp;ndash; default 151 &amp;ndash; and when you hit it, new queries fail.&lt;/p&gt;
&lt;p&gt;I hit this in production. The app was handling maybe forty concurrent requests and MySQL started refusing connections. The error is &lt;code&gt;Too many connections&lt;/code&gt; and there&amp;rsquo;s no graceful fallback. Queries fail.&lt;/p&gt;</description></item><item><title>URL mapping in Redis</title><link>https://blog.vnykmshr.com/writing/url-mapping-in-redis/</link><pubDate>Sun, 10 Jun 2012 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/url-mapping-in-redis/</guid><description>&lt;p&gt;I&amp;rsquo;m working on an e-commerce app in Node.js. The store has SEO-friendly URLs &amp;ndash; &lt;code&gt;/blue-widget/SKU12345/p/&lt;/code&gt; for products, &lt;code&gt;/category-name.html&lt;/code&gt; for categories, &lt;code&gt;/brand-name/&lt;/code&gt; for brands. Those URLs are indexed, bookmarked, linked from everywhere. They can&amp;rsquo;t change.&lt;/p&gt;
&lt;p&gt;Express routes are defined in code. The catalog has enough products, categories, and brands that I&amp;rsquo;m not writing a route for each one. I need a way to map the friendly URL to an internal route at runtime.&lt;/p&gt;</description></item><item><title>Rolling your own search</title><link>https://blog.vnykmshr.com/writing/rolling-your-own-search/</link><pubDate>Wed, 14 Mar 2012 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/rolling-your-own-search/</guid><description>&lt;p&gt;The shop needed catalog search. Users type something, products come back. Sounds trivial until you start building it.&lt;/p&gt;
&lt;p&gt;Our stack is Node.js, MySQL for the primary data store, MongoDB for everything else we need to go fast. The catalog lives in MySQL &amp;ndash; products, categories, attributes, prices. Normalized, relational, correct. But relational isn&amp;rsquo;t searchable. Try finding &amp;ldquo;blue cotton kurta&amp;rdquo; across five joined tables with MySQL FULLTEXT on MyISAM. It sort of works. The relevance is terrible.&lt;/p&gt;</description></item><item><title>Flying Thru the Motions</title><link>https://blog.vnykmshr.com/writing/flying-thru-the-motions/</link><pubDate>Mon, 11 Apr 2011 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/flying-thru-the-motions/</guid><description>&lt;p&gt;Three months in. Morning walk around the block with whoever&amp;rsquo;s up, ending at the tea stall. Tea at 7:30, maybe 8. Office by 9.&lt;/p&gt;
&lt;p&gt;Delhi is already hot &amp;ndash; April heat, the kind that sits on your chest when you step outside.&lt;/p&gt;
&lt;p&gt;I write code. I take a call with clients I have never met. I ship a release in the afternoon. I debug something small and stupid by 5. I am a dev and a deployment engineer and the guy on the call at once, and it has stopped feeling strange.&lt;/p&gt;</description></item><item><title>Tangled Up in Blue</title><link>https://blog.vnykmshr.com/writing/tangled-up-in-blue/</link><pubDate>Sun, 10 Apr 2011 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/tangled-up-in-blue/</guid><description>&lt;p&gt;Dylan does something with perspective that nobody else has figured out. First person becomes third person mid-verse. The timeline folds. You&amp;rsquo;re never sure who&amp;rsquo;s talking, and it doesn&amp;rsquo;t matter, because the feeling is consistent even when the narrator isn&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;He said it himself &amp;ndash; he was trying to deal with the concept of time, and the way characters shift between first and third person. &amp;ldquo;But as you look at the whole thing it really doesn&amp;rsquo;t matter.&amp;rdquo; That&amp;rsquo;s the line that made me pay attention differently. Most songwriters build a story. Dylan builds a space and lets you wander through it.&lt;/p&gt;</description></item><item><title>Naagin</title><link>https://blog.vnykmshr.com/writing/naagin/</link><pubDate>Mon, 28 Mar 2011 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/naagin/</guid><description>&lt;p&gt;The problem with most Indian rock is that it&amp;rsquo;s rock that happens to be played by Indians. Western structures, Western tone, maybe a tabla dropped in for colour. Fusion as a costume.&lt;/p&gt;
&lt;p&gt;Decibel&amp;rsquo;s &amp;ldquo;Naagin&amp;rdquo; is something else. It sounds like it grew out of this soil. The Lady Cobra &amp;ndash; pulled straight from mythology, not as decoration but as the spine of the song. The riff has weight. The vocal has bite. It doesn&amp;rsquo;t ask permission to be Indian and it doesn&amp;rsquo;t perform Indian-ness for a Western ear. It just is.&lt;/p&gt;</description></item><item><title>Chandigarh</title><link>https://blog.vnykmshr.com/writing/chandigarh/</link><pubDate>Sun, 06 Mar 2011 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/chandigarh/</guid><description>&lt;p&gt;I spent a few days in Chandigarh.&lt;/p&gt;
&lt;p&gt;Everything about it was right. Wide roads that don&amp;rsquo;t choke. Clean streets. Greenery that wasn&amp;rsquo;t an afterthought. The kind of city that teaches you what your own city got wrong.&lt;/p&gt;
&lt;p&gt;I felt nothing.&lt;/p&gt;
&lt;p&gt;Not depressed, not unhappy &amp;ndash; just disconnected from my own responses. My reactions were running a few frames behind whatever I was looking at. I was there with people I hadn&amp;rsquo;t seen in a long time. Even that wasn&amp;rsquo;t enough.&lt;/p&gt;</description></item><item><title>Ping from Noida</title><link>https://blog.vnykmshr.com/writing/ping-from-noida/</link><pubDate>Fri, 07 Jan 2011 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/ping-from-noida/</guid><description>&lt;p&gt;Hello from a slightly chilly Noida.&lt;/p&gt;
&lt;p&gt;I left Hyderabad on Sunday afternoon. Landed Sunday evening. Joined the team Monday morning. Five days in. Still finding the bathrooms.&lt;/p&gt;
&lt;p&gt;Hyderabad runs warm. Delhi runs to extremes &amp;ndash; both ends of the year. I landed in peak winter with nothing for it. The first thing I bought was a jacket. The second thing was a thicker one.&lt;/p&gt;
&lt;p&gt;The office looks like the comp lab from college &amp;ndash; fewer machines, full internet for once. I have a desk. I have a login. I have someone who wants me to ship something by next week. Everything else can wait.&lt;/p&gt;</description></item><item><title>The Cosmic Drummer</title><link>https://blog.vnykmshr.com/writing/the-cosmic-drummer/</link><pubDate>Tue, 04 Jan 2011 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/the-cosmic-drummer/</guid><description>&lt;p&gt;A friend of mine &amp;ndash; college senior, the kind of person who turns a chai break into a two-hour philosophy session &amp;ndash; once reframed Lord Shiva for me in a way I haven&amp;rsquo;t been able to shake.&lt;/p&gt;
&lt;p&gt;Start with the surface. Long hair. Snake around the neck. A drum. Followers. Performance art that shakes the cosmos. Lives outside every social norm. Shiva might be the original rock star, and I&amp;rsquo;m not sure anyone&amp;rsquo;s topped the aesthetic since.&lt;/p&gt;</description></item><item><title>Goodbye 2010, Goodbye Hyderabad</title><link>https://blog.vnykmshr.com/writing/goodbye-2010-goodbye-hyderabad/</link><pubDate>Sat, 01 Jan 2011 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/goodbye-2010-goodbye-hyderabad/</guid><description>&lt;p&gt;New Year. Half-packed boxes. I am leaving Hyderabad.&lt;/p&gt;
&lt;p&gt;Six and a half years here. Four in college on the city&amp;rsquo;s edge, two and a half working downtown. The first job out of college. The first bike. A flat full of friends &amp;ndash; like-minded, carefree, the kind you don&amp;rsquo;t need a reason to stay up with. Concerts that ran past midnight. The city where I learned what it felt like to be free.&lt;/p&gt;</description></item><item><title>Rare Pictures of Hyderabad</title><link>https://blog.vnykmshr.com/writing/rare-pictures-of-hyderabad/</link><pubDate>Thu, 16 Dec 2010 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/rare-pictures-of-hyderabad/</guid><description>&lt;p&gt;I stumbled onto a Flickr photostream of old Hyderabad. Black and white. Nizam era. Half of it showing monuments I&amp;rsquo;ve never been to.&lt;/p&gt;
&lt;p&gt;Two and a half years here. Some of those places are ten minutes from the office. I&amp;rsquo;ve walked past the turnoffs for most of them. I never turned.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s an ugly thing to admit. You can live in a place and not see it. You treat it like a room you&amp;rsquo;re passing through to get to the next thing.&lt;/p&gt;</description></item><item><title>The Chaos</title><link>https://blog.vnykmshr.com/writing/the-chaos/</link><pubDate>Sat, 04 Dec 2010 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/the-chaos/</guid><description>&lt;p&gt;I don&amp;rsquo;t remember where I first found this poem. Someone shared it, or I stumbled into it &amp;ndash; doesn&amp;rsquo;t matter. What matters is that I tried reading it aloud and didn&amp;rsquo;t make it past the first stanza without tripping.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;The Chaos&amp;rdquo; was written by a Dutch teacher named Gerard Nolst Trenite sometime around 1920. It&amp;rsquo;s 800 lines long. Every line exists to prove that English pronunciation follows no rules, respects no patterns, and will humiliate you if you try to read it with confidence.&lt;/p&gt;</description></item><item><title>Fast Car</title><link>https://blog.vnykmshr.com/writing/fast-car/</link><pubDate>Sun, 21 Nov 2010 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/fast-car/</guid><description>&lt;p&gt;&amp;ldquo;Fast Car&amp;rdquo; landed on me this week, and it won&amp;rsquo;t leave.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d heard it before &amp;ndash; everyone has. But there&amp;rsquo;s a difference between hearing it and having it land. Nothing&amp;rsquo;s wrong. Nothing concrete has changed. But the song has been playing in my head for days, and its sentence keeps finding me: &lt;em&gt;you gotta make a decision, you leave tonight or you live and die this way&lt;/em&gt;. I don&amp;rsquo;t have a decision to make. Not yet. The song knows something I don&amp;rsquo;t.&lt;/p&gt;</description></item><item><title>Clean designs</title><link>https://blog.vnykmshr.com/writing/clean-designs/</link><pubDate>Thu, 28 Oct 2010 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/clean-designs/</guid><description>&lt;p&gt;Two years ago I walked into a classroom in our office. Not a meeting room. An actual classroom, whiteboard, rows of chairs. The company had hired an instructor to teach the new hires Java. Two hours a day, three days a week, for three months.&lt;/p&gt;
&lt;p&gt;The training gave me syntax. Objects and classes. How inheritance works. What an interface is. The words for the things you do in code. Every week we wrote a small program and the instructor reviewed it.&lt;/p&gt;</description></item><item><title>Love, Life, Nostalgia</title><link>https://blog.vnykmshr.com/writing/love-life-nostalgia/</link><pubDate>Sun, 10 Oct 2010 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/love-life-nostalgia/</guid><description>&lt;p&gt;I am writing this down because I&amp;rsquo;m afraid I&amp;rsquo;ll soften it later.&lt;/p&gt;
&lt;p&gt;Coincidence. That&amp;rsquo;s all anything ever is. Nothing more than coincidence.&lt;/p&gt;
&lt;p&gt;There are no miracles. There is no such thing as fate. Nothing is meant to be.&lt;/p&gt;
&lt;p&gt;I have always known this.&lt;/p&gt;
&lt;p&gt;I am sure of it now.&lt;/p&gt;</description></item><item><title>Monologue of an Idiotic Soul</title><link>https://blog.vnykmshr.com/writing/monologue-of-an-idiotic-soul/</link><pubDate>Sat, 09 Oct 2010 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/monologue-of-an-idiotic-soul/</guid><description>&lt;p&gt;Some nights the code compiles fine and you still feel like a fraud.&lt;/p&gt;
&lt;p&gt;Not the dramatic kind. Not the kind where someone&amp;rsquo;s about to find you out. The quiet kind, where you fixed the bug and shipped the feature and everyone moved on, and you&amp;rsquo;re sitting there thinking &amp;ndash; did I actually understand what I just did, or did I just get lucky again?&lt;/p&gt;
&lt;p&gt;I think I understand this system.
Do I really, though.
I&amp;rsquo;ve been doing this for a couple of years now.
And yet I spent three hours on something that turned out to be a missing semicolon.
That could happen to anyone.
Could it, though.&lt;/p&gt;</description></item><item><title>Standing at the Starting Line</title><link>https://blog.vnykmshr.com/writing/standing-at-the-starting-line/</link><pubDate>Thu, 07 Oct 2010 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/writing/standing-at-the-starting-line/</guid><description>&lt;p&gt;I have a blank page and a working install. WordPress, on a shared host. The files are mine, the database is mine, the domain is mine. The first post should be written by now.&lt;/p&gt;
&lt;p&gt;It isn&amp;rsquo;t. I&amp;rsquo;ve been picking fonts.&lt;/p&gt;
&lt;p&gt;I struggled with what the first post should be. Prolific. Grand in nature. Broad in scope. I would spend another hour on themes, colors, the header layout &amp;ndash; everything had to be perfect before I could write a single sentence. I struggled some more.&lt;/p&gt;</description></item><item><title>About</title><link>https://blog.vnykmshr.com/about/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://blog.vnykmshr.com/about/</guid><description>&lt;p&gt;Vinayak Mishra. Been building backend systems for a while &amp;ndash; payments, risk, the parts where getting it wrong costs real money.&lt;/p&gt;
&lt;p&gt;Lately spending time on open-source security. Finding bugs in code everyone trusts, fixing what I can, disclosing the rest. Most of my own work lives at &lt;a href="https://github.com/1mb-dev"&gt;1mb-dev&lt;/a&gt; &amp;ndash; small Go and Node libraries, nothing fancy, just tools that do one thing well.&lt;/p&gt;
&lt;p&gt;Writing here is how I think through the work &amp;ndash; technical or otherwise. The patterns that hold, the ones that don&amp;rsquo;t, what I&amp;rsquo;d do differently next time.&lt;/p&gt;</description></item></channel></rss>