<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Hacker News: nanolith</title><link>https://news.ycombinator.com/user?id=nanolith</link><description>Hacker News RSS</description><docs>https://hnrss.org/</docs><generator>hnrss v2.1.1</generator><lastBuildDate>Sun, 24 May 2026 18:06:03 +0000</lastBuildDate><atom:link href="https://hnrss.org/user?id=nanolith" rel="self" type="application/rss+xml"></atom:link><item><title><![CDATA[New comment by nanolith in "Prefer do notation over Applicative operators when assembling records (2024)"]]></title><description><![CDATA[
<p>There is plenty of room for debate over this advice. Prefer do notation if the effects of the flow are more important than the data structure, and prefer using Applicative style if the data structure layout is more important to understand than the effects to build it. In the example in the article, the software is easier to understand from the perspective of how data was acquired to populate the record. However, this isn't true in a general sense; often records are purpose built in higher level languages to reflect effects and constrain them.<p>Example: when using Parsec or similar, Applicative style is far easier to understand when examining how records are parsed. In this case, the records will likely reflect the AST, which in turn will reflect the grammar. The two reinforce each other: additional constraints on syntax will naturally flock to data constructors.</p>
]]></description><pubDate>Thu, 02 Apr 2026 23:45:39 +0000</pubDate><link>https://news.ycombinator.com/item?id=47621679</link><dc:creator>nanolith</dc:creator><comments>https://news.ycombinator.com/item?id=47621679</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=47621679</guid></item><item><title><![CDATA[New comment by nanolith in "We X-Rayed a Suspicious FTDI USB Cable"]]></title><description><![CDATA[
<p>That's true. The only advantage of writing a driver in this case is if I wanted to add functions, such as a programmable level shifter.</p>
]]></description><pubDate>Sun, 25 Jan 2026 05:17:56 +0000</pubDate><link>https://news.ycombinator.com/item?id=46750981</link><dc:creator>nanolith</dc:creator><comments>https://news.ycombinator.com/item?id=46750981</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=46750981</guid></item><item><title><![CDATA[New comment by nanolith in "We X-Rayed a Suspicious FTDI USB Cable"]]></title><description><![CDATA[
<p>I could spot the clone because I'm familiar with the form factor of the FTDI IC, and I'm familiar enough with the datasheet to spot the expected passives.<p>I'm not too keen these days with FTDI's reputation for manipulating their Windows device drivers to brick clones. So, while I'm familiar with their IC, I don't give them any more money. The next time I need a USB to serial cable, I'll bust out KiCad to build it using one of the ubiquitous ARM microcontrollers with USB features built in. Of course, this is easier for me, since I can write my own Linux or BSD device driver as well. Those using OSes with signing restrictions on drivers would have a harder time, unless they chose to disable driver signing.</p>
]]></description><pubDate>Sun, 25 Jan 2026 03:53:27 +0000</pubDate><link>https://news.ycombinator.com/item?id=46750533</link><dc:creator>nanolith</dc:creator><comments>https://news.ycombinator.com/item?id=46750533</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=46750533</guid></item><item><title><![CDATA[New comment by nanolith in "TTY and Buffering"]]></title><description><![CDATA[
<p>In libc, you can use setvbuf to change the buffering mode.</p>
]]></description><pubDate>Thu, 22 Jan 2026 19:08:47 +0000</pubDate><link>https://news.ycombinator.com/item?id=46723748</link><dc:creator>nanolith</dc:creator><comments>https://news.ycombinator.com/item?id=46723748</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=46723748</guid></item><item><title><![CDATA[New comment by nanolith in "I'm just having fun"]]></title><description><![CDATA[
<p>As per my original comment, these examples are only indicative that profitable endeavors can come out of these things in unexpected ways, but that's not the point of doing these things. I'm never going to profit from, nor recoup the costs I've sunk into most of the mad science I do. That's not the point. I do it because it's fun and because I like building cool things.<p>These examples are one justification for why we should embrace these kinds of hobbies, and not the desirable outcome for these kinds of hobbies.</p>
]]></description><pubDate>Mon, 22 Dec 2025 19:04:30 +0000</pubDate><link>https://news.ycombinator.com/item?id=46357511</link><dc:creator>nanolith</dc:creator><comments>https://news.ycombinator.com/item?id=46357511</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=46357511</guid></item><item><title><![CDATA[New comment by nanolith in "I'm just having fun"]]></title><description><![CDATA[
<p>That is an excellent way of considering both leisure and work, and certainly, a testament to the importance of studying the humanities.<p>Aristotle famously developed the Greek concept of εὐδαιμονία (eudaimonia), which dovetails into what you wrote. Roughly, the concept translates into "human flourishing" or "living well". While Aristotle's conception of what best constitutes this differed a bit from more ancient Greek concepts passed down through their oral tradition, and definitely differs from what we may consider today, it bears investigation. I definitely think that education and personal research fits into my conception of it, but tastes differ. Nietzsche gave what I considered some excellent responses to Aristotle, especially when it comes to finding / making meaning in our lives with respect to the modern world. The Transcendentalist school, in particular Henry David Thoreau and Ralph Waldo Emerson, also provided some interesting flavor.<p>I think that your questions should be asked continuously. We should all adjust our life trajectories based on our own flourishing, in ways that challenge us and lead to growth. There aren't clear answers to these questions. In fact, they should lead to a bit of discomfort, like sand in one's clam shell. Much as this sand will eventually form a pearl, these questions should drive us to better ourselves, little by little.</p>
]]></description><pubDate>Mon, 22 Dec 2025 17:08:03 +0000</pubDate><link>https://news.ycombinator.com/item?id=46356028</link><dc:creator>nanolith</dc:creator><comments>https://news.ycombinator.com/item?id=46356028</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=46356028</guid></item><item><title><![CDATA[New comment by nanolith in "I'm just having fun"]]></title><description><![CDATA[
<p>Allegedly, it was given a lower grade due to it not being a feasible business plan, in the professor's estimation. Of course, this forms part of the legend behind Fred Smith and FedEx, so that should be taken with a grain of salt.<p><a href="https://finance.yahoo.com/news/fred-smith-told-yale-professor-213146704.html" rel="nofollow">https://finance.yahoo.com/news/fred-smith-told-yale-professo...</a></p>
]]></description><pubDate>Mon, 22 Dec 2025 16:47:14 +0000</pubDate><link>https://news.ycombinator.com/item?id=46355755</link><dc:creator>nanolith</dc:creator><comments>https://news.ycombinator.com/item?id=46355755</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=46355755</guid></item><item><title><![CDATA[New comment by nanolith in "I'm just having fun"]]></title><description><![CDATA[
<p>We need more people in this world willing to do their own thing, even if others might find it intimidating or silly. The important thing is to have fun and learn things. Compiler hacking is just as good as any other hobby, even if it's done in good jest.<p>Sometimes, these things become real businesses. Not that this should be the intent of this, but it shows that what some consider silly, others will pay good money for.<p>Example: Cards Against Humanity started as a bit of a gag game between a small group of friends and eventually became something that has pop culture relevance.<p>Example: The founder of FedEx actually wrote a business pitch paper for an overnight shipping company. This paper was given a low grade by his professor. He went on to form this company, which become a success, despite this low grade. I like to think that he did this out of spite, and that Christmas letters to his old professor must've been fun.</p>
]]></description><pubDate>Sun, 21 Dec 2025 23:36:59 +0000</pubDate><link>https://news.ycombinator.com/item?id=46349774</link><dc:creator>nanolith</dc:creator><comments>https://news.ycombinator.com/item?id=46349774</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=46349774</guid></item><item><title><![CDATA[New comment by nanolith in "The Coming Need for Formal Specification"]]></title><description><![CDATA[
<p>I'm starting to. One of the libraries I've started on recently is open source. I'm still really early in that process, but I started extracting a few functions for the optimized single and double linked list containers and will be moving onto the red-black and AVL tree containers soon. Once these are done, I should be able to model check the thread, fiber, and socket I/O components.</p>
]]></description><pubDate>Sat, 13 Dec 2025 19:31:55 +0000</pubDate><link>https://news.ycombinator.com/item?id=46257191</link><dc:creator>nanolith</dc:creator><comments>https://news.ycombinator.com/item?id=46257191</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=46257191</guid></item><item><title><![CDATA[New comment by nanolith in "The Coming Need for Formal Specification"]]></title><description><![CDATA[
<p>I have been formally verifying software written in C for a while now.<p>> is that only for some problems is the specification simpler than the code.<p>Indeed. I had to fall back to using a proof assistant to verify the code used to build container algorithms (e.g. balanced binary trees) because the problem space gets really difficult in SAT when needing to verify, for instance, memory safety for any arbitrary container operation. Specifying the problem and proving the supporting lemmas takes far more time than proving the code correct with respect to this specification.<p>> If you do this right, you can get over 90% of proofs with a SAT solver<p>So far, in my experience, 99% of code that I've written can be verified via the CBMC / CProver model checker, which uses a SAT solver under the covers. So, I agree.<p>I only need to reach for CiC when dealing with things that I can't reasonably verify by squinting my eyes with the model checker. For instance, proving containers correct with respect to the same kinds of function contracts I use in model checking gets dicey, since these involve arbitrary and complex recursion. But, verifying that code that uses these containers is actually quite easy to do via shadow methods. For instance, with containers, we only really care whether we can verify the contracts for how they are used, and whether client code properly manages ownership semantics. For instance, placing an item into the container or taking an item out of a container. Referencing items in the container. Not holding onto dangling references once a lock on a container is released, etc. In these cases, simpler models for these containers that can be trivially model checked can be substituted in.<p>> Now, sometimes you just want a negative specification - X must never happen. That's somewhat easier.<p>Agreed. The abstract machine model I built up for C is what I call a "glass machine". Anything that might be UB or that could involve unsafe memory access causes a crash. Hence, quantified over any acceptable initial state and input parameters that match the function contract, these negative specifications must only step over all instructions without hitting a crash condition. If a developer can single step, and learns how to perform basic case analysis or basic induction, the developer can easily walk proofs of these negative specifications.</p>
]]></description><pubDate>Sat, 13 Dec 2025 07:25:09 +0000</pubDate><link>https://news.ycombinator.com/item?id=46252770</link><dc:creator>nanolith</dc:creator><comments>https://news.ycombinator.com/item?id=46252770</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=46252770</guid></item><item><title><![CDATA[New comment by nanolith in "An SVG is all you need"]]></title><description><![CDATA[
<p>Sadly, I did not. I have the source code on an old laptop somewhere. I was disheartened when I considered productizing it and discovered just how deep of a patent tarpit I was dealing with.<p>It's on my list to revisit in the future. At this point, most of the patents are coming up on expiration, and it would make for a great open source project. Hardware has gotten much better over the subsequent years; there are nicer lower power solutions with integrated Bluetooth LE as well as other low power wireless technologies.</p>
]]></description><pubDate>Fri, 12 Dec 2025 20:41:27 +0000</pubDate><link>https://news.ycombinator.com/item?id=46248642</link><dc:creator>nanolith</dc:creator><comments>https://news.ycombinator.com/item?id=46248642</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=46248642</guid></item><item><title><![CDATA[New comment by nanolith in "An SVG is all you need"]]></title><description><![CDATA[
<p>Around 15 years ago, I built a barbecue controller. This controller had four temperature probes that could be used to check the temperature of the inner cooking chamber as well as various cuts of meat. It controlled servos that opened and closed vents and had a custom derived PID algorithm that could infer the delayed effects of oxygen to charcoal.<p>Anyway, of relevance to this thread is that the controller connected to the local wireless network and provided an embedded HTTP server with an SVG based web UI that would graph temperatures and provided actual knobs and dials so that the controller could be tweaked. SVG in the browser works nicely with Javascript.</p>
]]></description><pubDate>Thu, 11 Dec 2025 22:47:09 +0000</pubDate><link>https://news.ycombinator.com/item?id=46238400</link><dc:creator>nanolith</dc:creator><comments>https://news.ycombinator.com/item?id=46238400</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=46238400</guid></item><item><title><![CDATA[New comment by nanolith in "Problems with C++ exceptions"]]></title><description><![CDATA[
<p>The function contracts are integrated into the codebase. Bounded model checking tools, such as CBMC, can be used to check for integer UB, memory safety, and to evaluate custom user assertions. The latter feature opens the door for creating function contracts.<p>I include function contracts as part of function declarations in headers. These take the form of macros that clearly define the function contract. The implementation of the function evaluates the preconditions at the start of the function, and is written with a single exit so the postconditions can be evaluated at the end of the function. Since this function contract is defined in the header, shadow functions can be written that simulate all possibilities of the function contract. The two are kept in sync because they both depend on the same header. This way, model checks can be written to focus on individual functions with any dependencies simulated by shadows.<p>The model checks are included in the same project, but are separate from the code under instrumentation, similar to how unit tests are commonly written. I include the shadow functions as an installation target for the library when it is installed in development mode, so that downstream projects can use existing shadow functions instead of writing their own.</p>
]]></description><pubDate>Wed, 12 Nov 2025 07:14:03 +0000</pubDate><link>https://news.ycombinator.com/item?id=45897214</link><dc:creator>nanolith</dc:creator><comments>https://news.ycombinator.com/item?id=45897214</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=45897214</guid></item><item><title><![CDATA[New comment by nanolith in "Problems with C++ exceptions"]]></title><description><![CDATA[
<p>In C, you're correct. The problem is that, in C++, one must account for the fact that anything could throw an exception. If something throws an exception between the time that f is opened and f is closed, the file handle is leaked.  This is the "unsafe" that Bjarne is talking about here. Specifically, exception unsafety that can leak resources.<p>As an aside, it is one of the reasons why I finally decided to let go of C++ after 20 years of use. It was just too difficult to teach developers all of the corner cases. Instead, I retooled my system programming around C with model checking to enforce resource management and function contracts. The code can be read just like this example <i>and</i> I can have guaranteed resource management that is enforced at build time by checking function contracts.</p>
]]></description><pubDate>Wed, 12 Nov 2025 06:40:01 +0000</pubDate><link>https://news.ycombinator.com/item?id=45897028</link><dc:creator>nanolith</dc:creator><comments>https://news.ycombinator.com/item?id=45897028</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=45897028</guid></item><item><title><![CDATA[New comment by nanolith in "Man still alive six months after pig kidney transplant"]]></title><description><![CDATA[
<p>I'm living with heart failure. I have 20-30 years before I'll need a transplant, if I live a perfect lifestyle and keep my other health issues under control. Due to my other health issues, I am not a good candidate for a human heart transplant. It's not that a human heart transplant would fail, but that when I'd be placed against others on the list for a new heart, my other health issues would reduce my priority such that there is always someone with higher priority to receive a heart, up until the point in which I'm no longer healthy enough to receive a transplant. There are far too few human hearts, and far too many people who need one. All that the transplant boards can do is give hearts to those with the greatest momentary need, with the best chance of surviving.<p>Xenotransplantation is one of the life lines I'm counting on. I'm hoping that, by the time I need it, the issues that we currently have will be worked out. I have zero ethical issues with breeding and eventually culling pigs in order to save human lives. I hope that there will be other, better, breakthroughs by then, but if not, the best I can hope for is that the pigs are raised in a sterile and enriching environment, and that the only bad day they have is their last day.</p>
]]></description><pubDate>Sun, 28 Sep 2025 20:42:22 +0000</pubDate><link>https://news.ycombinator.com/item?id=45407798</link><dc:creator>nanolith</dc:creator><comments>https://news.ycombinator.com/item?id=45407798</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=45407798</guid></item><item><title><![CDATA[New comment by nanolith in "Why use mailing lists?"]]></title><description><![CDATA[
<p>I have actually argued for the use of mailing lists for corporate engineering discussions. When that becomes the medium for code review or design discussions, there's a nice streamlined workflow. Further, it's practically trivial to write or customize a mailing list reflector. If you have a decent and secure mail client library, you're a weekend away from it just working. Contrast that with customizing or rolling your own IRC, Slack, Discord, or web forum clone. Mailing lists don't suffer from vendor lock-in, and anyone with a mail client and who can follow basic rules can participate.<p>An invitation-only mailing list with a reflector that verifies PGP encryption and non-repudiation is just fine for most corporate discussions. For mailing lists open to the public, new users can be placed in a moderation queue for a period of time until it's clear that they understand list netiquette and formatting rules.</p>
]]></description><pubDate>Sat, 27 Sep 2025 00:28:32 +0000</pubDate><link>https://news.ycombinator.com/item?id=45392391</link><dc:creator>nanolith</dc:creator><comments>https://news.ycombinator.com/item?id=45392391</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=45392391</guid></item><item><title><![CDATA[New comment by nanolith in "Git: Introduce Rust and announce it will become mandatory in the build system"]]></title><description><![CDATA[
<p>If we mark any case that <i>might</i> be undecidable as a failure case, and require that code be written that can be verified, then this is very much sidestepping undecidability by definition. Rust's borrow checker does the same exact thing. Write code that the borrow checker can't verify, and you'll get an error, even if it might be perfectly valid. That's by design, and it's absolutely a design meant to sidestep undecidability.<p>Yes, CBMC + C provides a higher ceiling. Coupling Kani with Rust results in the <i>exact same</i> ceiling as CBMC + C. Not a higher one. Kani compiles Rust to the same goto-C that CBMC compiles C to. Not a better one. The abstract model and theory that Kani provides is far more strict that what Rust provides with its borrow checker and static analysis. It's also more universal, which is why Kani works on both safe and unsafe Rust.<p>If you like Rust, great. Use it. But, at the point of coupling Kani and Rust, it's reaching safety parity with model checked C, and not surpassing it. That's fine. Similar safety parity can be reached with Ada + Spark, C++ and ESBMC, Java and JBMC, etc. There are many ways of reaching the same goal.<p>There's no need to pepper C with macros or to require a stronger type system with C to use CBMC and to get similar guarantees. Strong type systems do provide some structure -- and there's nothing wrong with using one -- but unless we are talking about building a dependent type system, such as what is provided with Lean 4, Coq, Agda, etc., it's not enough to add equivalent safety. A dependent type system also adds undecidability, requiring proofs and tactics to verify the types. That's great, but it's also a much more involved proposition than using a model checker. Rust's H-M type system, while certainly nice for what it is, is limited in what safety guarantees it can make. At that point, choosing a language with a stronger type system or not is a style choice. Arguably, it lets you organize software in a better way that would require manual work in other languages. Maybe this makes sense for your team, and maybe it doesn't. Plenty of people write software in Lisp, Python, Ruby, or similar languages with dynamic and duck typing. They can build highly organized and safe software. In fact, such software can be made safe, much as C can be made safe with the appropriate application of process and tooling.<p>I'm not defending C or attacking Rust here. I'm pointing out that model checking makes both safer than either can be on their own. As with my original reply, model checking is something different than static analysis, and it's something greater than what either vanilla C or vanilla Rust can provide on their own. Does safe vanilla Rust have better memory safety than vanilla C? Of course. Is it automatically safe against the two dozen other classes of attacks by default and without careful software development? No. Is it automatically safe against these attacks with model checking? Also no. However, we can use model checking to demonstrate the absence of entire classes of bugs -- each of these classes of bugs -- whether we model check software written in C or in Rust.<p>If I had to choose between model checking an existing codebase (git or the Linux kernel), or slowly rewriting it in another language, I'd choose the former every time. It provides, by far, the largest gain for the least amount of work.</p>
]]></description><pubDate>Sun, 21 Sep 2025 04:57:41 +0000</pubDate><link>https://news.ycombinator.com/item?id=45320175</link><dc:creator>nanolith</dc:creator><comments>https://news.ycombinator.com/item?id=45320175</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=45320175</guid></item><item><title><![CDATA[New comment by nanolith in "Git: Introduce Rust and announce it will become mandatory in the build system"]]></title><description><![CDATA[
<p>It's not used more because it is unknown, not because it is difficult to use or that it is impractical.<p>I've written several libraries and several services now that have 100% coverage via CBMC. I'm quite experienced with C development and with secure development, and reaching this point always finds a handful of potentially exploitable errors I would have missed. The development overhead of reaching this point is about the same as the overhead of getting to 80% unit test coverage using traditional test automation.</p>
]]></description><pubDate>Sat, 20 Sep 2025 22:45:41 +0000</pubDate><link>https://news.ycombinator.com/item?id=45318281</link><dc:creator>nanolith</dc:creator><comments>https://news.ycombinator.com/item?id=45318281</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=45318281</guid></item><item><title><![CDATA[New comment by nanolith in "Git: Introduce Rust and announce it will become mandatory in the build system"]]></title><description><![CDATA[
<p>The applicability of Rice's theorem with respect to static analysis or abstract interpretation is more complex than you implied. First, static analysis tools are largely pattern-oriented. Pattern matching is how they sidestep undecidability. These tools have their place, but they aren't trying to be the tooling you or the parent claim. Instead, they are more useful to enforce coding style. This can be used to help with secure software development practices, but only by enforcing idiomatic style.<p>Bounded model checkers, on the other hand, are this tooling. They don't have to disprove Rice's theorem to work. In fact, they work directly with this theorem. They transform code into state equations that are run through an SMT solver. They are looking for logic errors, use-after-free, buffer overruns, etc. But, they also fail code for unterminated execution within the constraints of the simulation. If abstract interpretation through SMT states does not complete in a certain number of steps, then this is also considered a failure. The function or subset of the program only passes if the SMT solver can't find a satisfactory state that triggers one of these issues, through any possible input or external state.<p>These model checkers also provide the ability for user-defined assertions, making it possible to build and verify function contracts. This allows proof engineers to tie in proofs about higher level properties of code without having to build constructive proofs of all of this code.<p>Rust has its own issues. For instance, its core library is unsafe, because it has to use unsafe operations to interface with the OS, or to build containers or memory management models that simply can't be described with the borrow checker. This has led to its own CVEs. To strengthen the core library, core Rust developers have started using Kani -- a bounded model checker like those available for C or other languages.<p>Bounded model checking works. This tooling can be used to make either C or Rust safer. It can be used to augment proofs of theorems built in a proof assistant to extend this to implementation. The overhead of model checking is about that of unit testing, once you understand how to use it.<p>It is significantly less expensive to teach C developers how to model check their software using CBMC than it is to teach them Rust and then have them port code to Rust. Using CBMC properly, one can get better security guarantees than using vanilla Rust. Overall, an Ada + Spark, CBMC + C, Kani + Rust strategy coupled with constructive theory and proofs regarding overall architectural guarantees will yield equivalent safety and security. I'd trust such pairings of process and tooling -- regardless of language choice -- over any LLM derived solutions.</p>
]]></description><pubDate>Sat, 20 Sep 2025 21:09:38 +0000</pubDate><link>https://news.ycombinator.com/item?id=45317575</link><dc:creator>nanolith</dc:creator><comments>https://news.ycombinator.com/item?id=45317575</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=45317575</guid></item><item><title><![CDATA[New comment by nanolith in "Betty Crocker broke recipes by shrinking boxes"]]></title><description><![CDATA[
<p>Much to the chagrin of my mother, I made it a point about a decade ago to standardize old family recipes on "from scratch" versions. As part of the process, I also did some research on old recipes and fixed some of the corruption of these recipes that occurred during the copying and recitation, bolstering them with culinary techniques that were in use at the time. I also captured things that drift over time, such as crude protein and carbohydrate measurements and grind sizes in flour. I provided standardized weights and measurements, in MKS units, preferring mass, when possible, over volume.<p>She's upset that the recipes are different, but when it comes to recipes from the thirties and later based on using a box of this or a can of that, these recipes are resistant to shrinkflation. The downside is that these recipes miss out on the advanced chemistry that went into making these boxed mixes so great to begin with. But, in my opinion, that's a small price to pay for reproducibility.<p>Some recipes, like cakes and cookies, will need to be adjusted once a generation. For these recipes, I include notes about how to tell when certain ingredients are "off" so that these can be re-calibrated as ingredients change in the future. Ingredients change. Some are no longer available. Others are derived from newer varieties or hybrids that have different flavor profiles. For instance, bananas taste differently than they did sixty years ago. That old and dusty banana pudding recipe meant to reproduce that amazing pudding that your great-grandmother used to make won't taste the same unless you adjust the amount of isoamyl acetate; modern varieties have less of this compound than the old Gros Michel varieties did. You can occasionally find Gros Michel bananas if you want to taste the difference, but they are no longer a viable cash crop due to their susceptibility to Panama disease.</p>
]]></description><pubDate>Sun, 14 Sep 2025 23:09:31 +0000</pubDate><link>https://news.ycombinator.com/item?id=45244230</link><dc:creator>nanolith</dc:creator><comments>https://news.ycombinator.com/item?id=45244230</comments><guid isPermaLink="false">https://news.ycombinator.com/item?id=45244230</guid></item></channel></rss>