[{"slug":"why-your-index-is-not-used","title":"Why Your Database Index Is Not Being Used","description":"A working guide to the reasons Postgres ignores an index you were sure it would use, and how to read the plan instead of guessing at it.","category":"Engineering","tags":["databases","postgres","performance","sql"],"tldr":"Postgres skips an index when it estimates that reading the table directly will cost less, when the query shape does not match the index's leading columns, or when a function or type cast on the indexed column makes the index inapplicable. Read EXPLAIN ANALYZE and compare estimated rows against actual rows before changing anything.","body":" You add the index. You rerun the query. Nothing changes. The plan still says `Seq Scan`, and the natural conclusion is that the planner has made a mistake. It almost never has. The planner is a cost model, and a cost model that refuses an index is telling you something specific about your data. This is a guide to reading what it is telling you. Every fix below depends on knowing which of the four causes you have. Running `EXPLAIN (ANALYZE, BUFFERS)` answers that in one command. Guessing does not. The four reasons an index goes unused There are more than four, but these cover the overwhelming majority of real cases.  The rest of this article works through each one. Cause 1: the row estimate is wrong Postgres decides between a sequential scan and an index scan by estimating how many rows the query will return. Index scans read the index, then jump to the heap for each match. Those jumps are random reads. Past roughly five to ten percent of the table, reading everything in order wins. So when the estimate is badly too high, the planner correctly chooses a sequential scan for a query that actually returns four rows.  Compare the two numbers in the plan output:  An estimate of 98,214 against an actual of 37 is the whole story. The planner did not have the information it needed. The usual causes are stale statistics after a bulk load, or a column whose distribution is more skewed than the default sample can capture.  Correlated columns are the harder version of this. Postgres assumes independence, so a filter on `city = 'Pune' AND state = 'Maharashtra'` has its selectivity multiplied out to something implausibly small. Extended statistics fix that:  Cause 2: the leading column does not match A composite index on `(tenant_id, created_at, status)` is a sorted structure. It can serve a query filtering on `tenant_id`, or on `tenant_id` and `created_at`, or on all three. It cannot efficiently serve a query filtering only on `status`, for the same reason a phone book sorted by"},{"slug":"typography-decisions-that-change-reading","title":"The Typography Decisions That Actually Change Reading","description":"Five typographic choices measurably change how much of a page people read. Most redesigns spend their effort on the other forty that do not.","category":"Design","tags":["typography","web design","readability","accessibility"],"tldr":"Reading comfort on the web is governed by a short list of decisions: measure, line height, size at the smallest breakpoint, contrast, and paragraph rhythm. Typeface choice matters far less than any of them, which is why a redesign that only swaps the font rarely changes how much people read.","body":" Most typography advice is about taste. This is about the five decisions that change whether someone finishes the page, ordered by how much they matter. Measure comes first Line length, or measure, is where reading either works or does not. Too long and the eye loses the return sweep, landing on the wrong line and forcing a re-read the reader is not conscious of. Too short and the sentence fragments into stutters.  The mistake is setting this in pixels. A 700px column is a comfortable 68 characters in one face and a punishing 95 in another. The `ch` unit measures the width of the digit zero in the font actually being used, so it tracks the type instead of guessing at it.  One line. It is the highest-leverage line of CSS in most stylesheets. Reference material that is scanned rather than read, such as API tables and changelogs, tolerates and often benefits from a wider measure. The 45 to 75 range is for prose someone reads in sequence. Line height is a function of measure, not a constant Leading and measure are one decision, not two. The wider the column, the more vertical separation the eye needs to find the next line reliably. A `line-height` of 1.5 that feels generous at 45 characters feels cramped at 75.  Headings run the other way. Large type at 1.5 falls apart into separate lines that no longer read as one phrase. Set `line-height` unitless. A unitless value is inherited as a ratio and recomputed against each element's own size; a value in `px` or `em` is inherited as a fixed distance and quietly ruins every nested element that is not the same size as its parent. Size, decided at the smallest breakpoint The common failure is designing body text on a large monitor and then scaling down. Do it the other way. Set the size that is comfortable on a phone held at arm's length, then let it grow. 16px is the floor for body text, not a target. Below it, mobile browsers may zoom on focus and readers pinch to compensate. 17px to 19px is where most long-form sites land for"},{"slug":"rag-is-not-a-database-problem","title":"RAG Is Not a Database Problem","description":"Teams reach for a vector database on day one and spend the next month discovering that retrieval quality was never about the store they picked.","category":"Artificial Intelligence","tags":["rag","retrieval","llm","search","evaluation"],"tldr":"Most retrieval-augmented generation systems fail at chunking, query formulation and ranking, not at vector storage. The database is the most interchangeable component in the stack, which is why swapping it rarely fixes anything. Build an evaluation set first, then fix retrieval in the order that the failures actually occur.","body":" The first architecture diagram is always the same. Documents on the left, an arrow labelled \"embed\", a vector database in the middle, an arrow to an LLM on the right. The database gets a logo and a name. Everything else is an arrow. Then retrieval quality is poor, and the team evaluates a different vector database. Start with the thing nobody wants to build Before touching retrieval, build the evaluation set. Fifty to two hundred real questions, each paired with the passages that genuinely answer it. This is tedious, requires domain knowledge, and produces no demo. It is also the only thing that makes every subsequent decision measurable rather than aesthetic. If you cannot answer \"did that change help?\" with a number, you are tuning by vibe. Every RAG system that stalls for a month is stalled here. Track two things at minimum: **Recall@k** — of the passages that should have been found, how many appeared in the top k? **Precision@k** — of the k passages retrieved, how many were actually relevant? Recall is the ceiling. If the right passage is not in the retrieved set, no amount of prompting produces a correct grounded answer. Fix recall before anything downstream. Failure 1: chunking destroys the answer before retrieval starts Fixed-size chunking is the default because it is easy, and it is the most damaging default in the pipeline. Split a document every 512 tokens and you will, reliably, cut a table from its header, separate a definition from the term it defines, and place a procedure's step 4 in a different chunk from steps 1 to 3. The information still exists in your corpus. It is no longer retrievable as an answer. Split on structure instead:  Two additions repay themselves immediately. Prepend the document title and heading path to each chunk, so an orphaned section still carries its context. And keep tables intact even when they exceed the cap, because a truncated table is worse than a long one. Failure 2: the question does not look like the answer A user as"},{"slug":"index-funds-are-boring-on-purpose","title":"Index Funds Are Boring on Purpose","description":"The case for passive investing is not that markets are efficient. It is that costs are certain, forecasts are not, and behaviour is the real variable.","category":"Personal Finance","tags":["investing","index funds","personal finance","compounding"],"tldr":"An index fund wins over time not because markets are perfectly efficient but because its costs are low and knowable while returns are neither. Fees compound against you exactly as returns compound for you, and the average active fund must underperform the market it is drawn from once its costs are subtracted.","body":" The argument for index funds is usually made badly. It is presented as a claim that markets are efficient and prices are always right, which is both contestable and beside the point. The actual argument is narrower and much harder to dispute: costs are certain and returns are not. The arithmetic, which is not a theory William Sharpe's observation about active management holds regardless of whether markets are efficient, because it is arithmetic rather than economics. All the shares in a market are held by someone. The passive investors hold their slice at market weight and earn, by construction, the market return minus a very small fee. Everyone else, in aggregate, holds the remaining slice, which is also at market weight. So active investors as a group earn the market return before costs. After costs, they earn less. There is no arrangement of skill among them that changes the aggregate. It does not say every active fund underperforms. It says the average dollar in active management must, after costs, trail the average dollar in passive management. Some funds beat the index. Their outperformance is funded by other active investors, not created from nothing. Fees compound against you The reason a one percent fee is worse than it sounds is that it is charged on the balance, every year, including on the growth that previous years' fees already reduced.   Figures are rounded and illustrative, and assume a constant 7 percent gross return with no contributions. The uncomfortable line is the last one. A two percent all-in cost, which is not unusual once a fund fee and an advisory fee are stacked, removes something close to two-fifths of the outcome. It does that without any bad year, any bad decision, or any market event.  Past performance is a story about luck The natural response is to pick the funds that have done well. This fails more reliably than intuition suggests. With enough funds in a market, some produce five strong years by chance alone. Nothing in the record"},{"slug":"zone-2-training-without-a-lab","title":"Zone 2 Training Without a Lab","description":"How to find your easy aerobic pace using conversation, breathing and a heart-rate strap, and why going slower is what makes the hard days work.","category":"Health","tags":["running","endurance","training","heart rate"],"tldr":"Zone 2 is the intensity at which you can hold a full conversation and breathe through your nose, roughly 60 to 70 percent of maximum heart rate. Most recreational athletes train above it almost every session, which is why they accumulate fatigue without accumulating aerobic base. You can find the zone reliably without lactate testing.","body":" The problem with easy running is that it is not comfortable to do. Not physically, which is the point, but socially and psychologically. Running slowly feels like not training. So most people don't. They run their easy days at a moderate effort that is too hard to recover from and too soft to drive adaptation, and then wonder why the hard sessions never improve. What zone 2 actually is Zone 2 is the upper end of the intensity range where your body clears lactate as fast as it produces it. Below it, effort is trivially sustainable. Above it, lactate accumulates, and the clock starts. The physiological target is mitochondrial density and fat oxidation — slow adaptations in slow-twitch fibres that need volume and time rather than intensity. Intensity develops other things. It does not develop this.  Three field tests, in order of reliability Forget the watch for a moment. The zone has a feel, and the feel is more reliable than the number. **The talk test.** You should be able to speak a full sentence, comfortably, without breaking it up to breathe. Not a few words. A sentence, with punctuation. If you find yourself editing what you were going to say to make it shorter, you are above zone 2. **Nasal breathing.** Close your mouth. If you can maintain the effort breathing only through your nose, you are at or below the top of zone 2. The moment you need your mouth, you have crossed. This one is unusually good because it fails immediately and unmistakably. **Heart rate.** Roughly 60 to 70 percent of true maximum. Useful, but third on the list for good reasons covered below. Run twenty minutes at a pace where the talk test passes cleanly, then look at your average heart rate. That number is your zone 2 ceiling, and it is more trustworthy than anything a formula produced. Why the watch is the least reliable of the three Heart rate is a lagging, drifting, context-dependent signal.  Cadence lock is the one that misleads people most: an optical wrist sensor loses the pulse and"},{"slug":"how-to-salt-food-properly","title":"Salt Is Four Decisions, Not One","description":"When you salt, which salt you use, how you distribute it, and how you taste for it are separate choices, and each one changes the result.","category":"Food","tags":["cooking","technique","seasoning","kitchen basics"],"tldr":"Salt is usually treated as one decision made at the end of cooking. It is four: timing, which changes texture through osmosis; crystal type, which changes how much salt a measured spoon actually contains; distribution, which decides whether seasoning is even; and tasting method, which decides whether you can judge any of the other three.","body":" Ask most home cooks when they salt and the answer is \"at the end, to taste\". That is one decision, made once, at the point where it has the least influence on anything except the surface. Salt is doing four different jobs, and they are made at four different moments. Decision 1: when This is the decision that changes texture rather than flavour, and it is the one most often skipped. Salt on the surface of meat draws moisture out by osmosis. That brine then dissolves surface proteins and, given time, is reabsorbed, carrying salt inward. The meat ends up seasoned through rather than seasoned on top, and it holds moisture better under heat. The timing rule follows directly from how long that cycle takes:  That fourth row is the trap. Five to thirty minutes is long enough for salt to pull moisture to the surface and not long enough for it to be reabsorbed, so you cook wet meat that steams instead of searing. Either well ahead, or right now. Not in between. Salt the meat when you take it out of the fridge to plan dinner, not when the pan is already hot. That single change in habit does more than any technique in this article. Decision 2: which salt This one is not about flavour, whatever the packaging says. Sodium chloride is sodium chloride, and the trace minerals in expensive salts are present in quantities you cannot taste dissolved in a stew. It is about density. Crystal shape determines how much salt fits in a spoon, and the spread is large:  **Table salt** — dense, fine cubes. Roughly 6g per teaspoon. Dissolves fast, easy to overshoot. **Kosher / coarse salt** — larger irregular crystals. Roughly 3 to 5g per teaspoon depending on brand. **Flaky finishing salt** — hollow pyramids. Roughly 3g per teaspoon, and it stays crunchy on the surface. Which is why a recipe calling for \"one teaspoon of salt\" is genuinely ambiguous, and why measuring by weight is not pedantry. If a recipe was written with table salt and you use coarse salt by volume, you are under-seasoning by"},{"slug":"heat-pumps-work-in-the-cold","title":"Heat Pumps Really Do Work in the Cold","description":"The objection that heat pumps fail in winter comes from equipment sold two decades ago. The physics, and the field data, now say something different.","category":"Climate","tags":["energy","heat pumps","efficiency","home"],"tldr":"A heat pump moves heat rather than creating it, so it can deliver more energy into a home than it consumes. Cold-climate models using variable-speed compressors sustain useful output well below freezing, and field studies in genuinely cold regions show them working. The remaining obstacles are installation quality and electricity pricing, not physics.","body":" The objection arrives in the same form every time: heat pumps are fine in mild climates, but they cannot cope with a real winter. It was true. It was true of single-speed equipment sold in the 1990s and early 2000s, and a lot of people formed a durable impression from a unit that genuinely could not manage a cold January. The equipment changed. The impression did not. Why the efficiency number looks impossible A gas boiler burns fuel and converts the chemical energy to heat. At best it captures nearly all of it, so its efficiency approaches but cannot exceed 100 percent. An electric resistance heater converts electricity to heat at essentially exactly 100 percent. A heat pump does not convert anything. It moves heat that already exists from outside to inside, and the electricity is spent on the moving, not the heat itself. So the ratio of heat delivered to electricity consumed is not bounded by 100 percent.  This is the same mechanism as a refrigerator, run in the direction that people find counterintuitive. A fridge takes heat out of a cold box and dumps it into a warmer kitchen. A heat pump takes heat out of cold outdoor air and dumps it into a warmer house. Neither violates anything; both need work input to move heat against the gradient. \"There is no heat in cold air\" is a scale error At minus fifteen degrees Celsius, outdoor air is about 258 kelvin. Absolute zero is 0 kelvin. The air contains a great deal of thermal energy by any absolute measure — it is simply colder than your living room, which is the only comparison our intuition makes. The refrigerant cycle exploits that. It evaporates at a temperature well below the outdoor air, so heat flows into it from air that feels frigid to us. Compressing that vapour raises its temperature above the indoor air, and the heat flows out. The compressor's work is what makes the gradient run the way you want. Efficiency and capacity both fall as it gets colder, because the temperature gap the compressor must bridge grow"},{"slug":"the-case-for-staying-longer","title":"The Case for Staying Longer in Fewer Places","description":"Four cities in ten days costs more, tires you out and shows you less. A slower itinerary is not an indulgence, it is the more efficient one.","category":"Travel","tags":["slow travel","itinerary","planning","budget"],"tldr":"Every move between cities costs most of a day and a meaningful share of the budget, while delivering nothing you came for. Cutting an itinerary from four bases to two typically buys back three usable days and reduces cost, because accommodation gets cheaper by the week and you stop paying tourist prices for everything.","body":" The itinerary arrives in the group chat with four cities and a lot of exclamation marks. Ten days. Rome, Florence, Venice, Milan. It will be a good trip. It will also be, roughly, six days of trip and four days of transit, at a higher price than the alternative. What a travel day actually costs Not the ticket. The day.  Six to eight hours, most of it spent standing next to a suitcase. Book an inconvenient departure and you lose the evening before as well, because you will not start anything you cannot finish.  Three days is not a rounding error on a ten-day trip. It is thirty percent of it. The money runs the same direction The intuition is that staying longer costs more, because more nights. It does not survive contact with the actual pricing. **Accommodation.** Weekly rates on apartments run well below seven times the nightly rate. Hotels discount for length of stay less aggressively, which is itself an argument for apartments. **Transport.** Every intercity leg is a fare. Four bases means three fares, plus six local taxis at either end. **Food.** Your first two meals in a new city are bought without information, which reliably means paying more for less. By day four you know where the good place is and what it costs. **Duplication.** Every base has its own arrival costs: groceries you buy and abandon, the transit pass you use twice, the twenty minutes lost to a wrong turn. Cooking two breakfasts a week in an apartment saves more over ten days than most people save by optimising flights, and takes no planning at all. What the second week gives you The first two days in any city are spent on the list. The famous things, in the order the internet ranked them. That is fine, and it is also the least distinctive experience available, because everyone is doing exactly the same sequence. Somewhere around day four, the list runs out. That is where most itineraries move you on, and it is precisely the point where something else becomes possible.  You start recognising a r"},{"slug":"writing-the-decision-not-the-decision-log","title":"Write Down the Decision, Not the Meeting","description":"Most team documentation records what was discussed. The thing that turns out to be valuable a year later is why the alternatives were rejected.","category":"Product","tags":["documentation","decision records","teams","writing"],"tldr":"Meeting notes record what was said; decision records capture what was chosen and why the alternatives were not. The second survives contact with the future, because the question people ask a year later is never what was discussed, it is whether the reasoning still holds. A useful record fits on one page and is written before the decision is executed.","body":" Open the wiki of any team more than two years old and you will find hundreds of pages of meeting notes. Attendees, agenda, discussion, action items. Faithfully recorded and almost never read. Now try to answer a question that comes up constantly: *why does the payment service talk to the ledger over a queue instead of directly?* The notes will not tell you. Somewhere in there is a meeting where it was decided, and the notes will say \"discussed queue vs. direct integration\" and then list an action item. The reasoning is gone. What a decision record captures that notes do not A decision record is not a summary of a conversation. It has a fixed shape, and each part exists because a specific future question needs it.  The third row carries most of the value and is the one most often left out. A record saying \"we chose a queue\" tells you nothing you cannot read from the code. A record saying \"we chose a queue because direct calls would have coupled deployment schedules across two teams, and we rejected the shared database option because the ledger's compliance requirements would have propagated to the payment service\" tells you when it is safe to revisit. Hand it to someone who joined last month and ask them to argue the opposite side. If they cannot, the alternatives section is too thin. Write it before you build, not after There is a strong pull toward writing these afterwards, when the decision is settled and the writing is easy. That is exactly what makes the result worthless. Once a decision is implemented, it acquires an air of inevitability. The alternatives that were genuinely competitive at the time get remembered as obviously worse. What you write down is not the reasoning, it is a justification, and justification is what makes future readers assume the question was never close.  There is a second benefit, which several teams have described to me independently. Writing the alternatives section honestly, before committing, sometimes changes the decision. It is "}]