# Newsletter feed for Hillel Wayne's Newsletter

Hi, I'm Hillel. This is the newsletter version of my website. I post all website updates here. I also post weekly content just for the newsletter, on topics like Formal Methods Software History and Culture Fringetech and exotic tooling The philosophy and theory of software engineering You can see the archive of all public essays here.

This is one page of public article previews, not the complete archive. Follow Next page to continue. Summaries are not the original full articles.

## Investigating whether LLMs disproportionately use "spine" in GitHub pull request titles

DevFeed: [Investigating whether LLMs disproportionately use "spine" in GitHub pull request titles](<https://devfeed.tech/articles/the-llms-yearn-for-the-spines-32318.md>)

Original publisher: [Read original article](<https://buttondown.com/hillelwayne/archive/the-llms-yearn-for-the-spines/>)

Author: Hillel Wayne

Published: 2026-09-16T19:58:30Z

Content type: opinion

Language: en

Sources: [Newsletter feed for Hillel Wayne's Newsletter](<https://devfeed.tech/sources/newsletter-feed-for-hillel-wayne-s-newsletter.md>)

Topics: [Large Language Model](<https://devfeed.tech/topics/llm.md>), [Artificial Intelligence](<https://devfeed.tech/topics/ai.md>), [GitHub](<https://devfeed.tech/topics/github.md>), [pull-requests](<https://devfeed.tech/topics/pull-requests.md>), [anthropic](<https://devfeed.tech/topics/anthropic.md>), [cursor](<https://devfeed.tech/topics/cursor.md>)

Tags: [2026](<https://devfeed.tech/tags/2026.md>), [ai](<https://devfeed.tech/tags/ai.md>), [claude](<https://devfeed.tech/tags/claude.md>), [cursor](<https://devfeed.tech/tags/cursor.md>), [github](<https://devfeed.tech/tags/github.md>), [llms](<https://devfeed.tech/tags/llms.md>), [pull-requests](<https://devfeed.tech/tags/pull-requests.md>)

### AI overview

The article investigates whether large language models disproportionately use the word "spine" in GitHub pull request titles. Using GitHub Search as a proxy, it finds a much larger increase in "spine" pull requests than overall pull requests, but does not establish that the pattern comes from one specific model; sampled pull requests were coauthored by Claude or Cursor.

### Source excerpt

Earlier this year I worked on a couple of AI-generated TLA+ specifications, and one thing I've noticed was that they all used the word "spine" somewhere. Then I saw some non-TLA+ public projects use "spine" too and got curious if it was a new LLMism (LLisM?). If it is, we should see it appear in a discontinuous jump in the number of code projects that use "spine". The proper way to thoroughly explore this would be via the GitHub archive data dump but that'd take forever and I have a full time job now. Instead, I'm going to use GitHub Search. We can't filter code samples by date but can filter pull requests, which seems like an acceptable enough proxy. Here's a graph of the number of public PRs each year with "spine" in the title: I stopped 2026 at 2026-09-01, meaning the first nine months of this year have seen 20 times more "spine" PRs as all of 2025. Now it could be the case that that GitHub grew, like, a lot in the last year. Maybe the 20x increase is due there being 20x as many pull requests. We can account for this by comparing it against all public PRs:1 If "spine" wasn't an LLisM, we would expect only a 1.5xish increase from 2025, not a 20x increase. One other possible confounder could be that LLMs don't like the word spine but PRs Georg submitted 15,000 changes to a chiropractor repo. I will deal with this issue by ignoring it. Can we figure out which models specifically are obsessed with spines? Let's try breaking the change down by month: Looking at LLM timeline, the only widely-used model released in May was Opus 4.8, which came out too late in the month to explain the spike. GPT 5.5 came out in April, which could be the cause. But I'm not convinced by this. For one, the spine count is still 1.5xing each month before April, and I definitely remember seeing spines as early as February. Also, I spot checked and many of the PRs that use "spine" are coauthored by Claude or Cursor. So I think it's not specific to one LLM, but could be convinced otherwise. Othe

## New Post: A Crash Course in Predicate Logic

DevFeed: [New Post: A Crash Course in Predicate Logic](<https://devfeed.tech/articles/new-post-a-crash-course-in-predicate-logic-25498.md>)

Original publisher: [Read original article](<https://buttondown.com/hillelwayne/archive/new-post-a-crash-course-in-predicate-logic/>)

Author: Hillel Wayne

Published: 2026-09-01T18:30:01Z

Content type: release

Language: en

Sources: [Newsletter feed for Hillel Wayne's Newsletter](<https://devfeed.tech/sources/newsletter-feed-for-hillel-wayne-s-newsletter.md>)

Topics: [Math and Logic](<https://devfeed.tech/topics/math-and-logic.md>)

Tags: [blog](<https://devfeed.tech/tags/blog.md>), [course](<https://devfeed.tech/tags/course.md>), [crash](<https://devfeed.tech/tags/crash.md>), [free](<https://devfeed.tech/tags/free.md>)

### AI overview

The author announces the release of a free second chapter from Logic for Programmers, titled "A Crash Course in Logic," on their blog. The chapter focuses on predicate logic.

### Source excerpt

Logic for Programmers has now been out for a month! To celebrate, I'm releasing the entire second chapter, "A Crash Course in Logic", for free on my blog. Read it here!

## Vim wants you to control, VSCode wants you to consume

DevFeed: [Vim wants you to control, VSCode wants you to consume](<https://devfeed.tech/articles/vim-wants-you-to-control-vscode-wants-you-to-consume-25508.md>)

Original publisher: [Read original article](<https://buttondown.com/hillelwayne/archive/vim-wants-you-to-control-vscode-wants-you-to/>)

Author: Hillel Wayne

Published: 2026-08-18T16:26:05Z

Content type: opinion

Language: en

Sources: [Newsletter feed for Hillel Wayne's Newsletter](<https://devfeed.tech/sources/newsletter-feed-for-hillel-wayne-s-newsletter.md>)

Topics: [Vim](<https://devfeed.tech/topics/vim.md>), [Neovim](<https://devfeed.tech/topics/neovim.md>), [Visual Studio Code](<https://devfeed.tech/topics/visual-studio-code.md>), [configuration](<https://devfeed.tech/topics/configuration.md>), [Emacs](<https://devfeed.tech/topics/emacs.md>)

Tags: [command-line](<https://devfeed.tech/tags/command-line.md>), [config](<https://devfeed.tech/tags/config.md>), [configuration](<https://devfeed.tech/tags/configuration.md>), [vim](<https://devfeed.tech/tags/vim.md>), [vscode](<https://devfeed.tech/tags/vscode.md>)

### AI overview

The article contrasts Vim and Neovim's programmable, state-oriented configuration with VSCode's static configuration and fixed built-in commands. It argues that Vim-based editors provide substantially more direct control over editor behavior, while also requiring more configuration and commitment.

### Source excerpt

Newsletter updates were sporadic in July because of two weddings, two conferences (with two different talks!), and finishing Logic for Programmers. Huge thank you to everybody who bought a copy, as well as for your patience with the schedule. There's some podcast appearances, a conf talk, and a book sale at the end of this post. Newsletter updates will be sporadic in August because I just started my Developer Educator job at Antithesis. I'll have less time to write because I'll be working 40 hour workweeks, about 8 hours of which being actual work and the other 32 being bashing my head against NixOS. NixOS is the standard developer OS at the company. It's also a notoriously difficult distro to learn even for Linux heads, and I'm coming from Windows. The only way I am going to get anywhere is to go all in and commit fully to the NixOS philosophy.1 For one, I'm seeing how long I can last without my customary 2000-line Neovim config. Which immediately raises the question as to why I have 2000 lines of Neovim config. It's because Vim2 (and Emacs) think of configuration in a very different way than more popular editors do. Control and Consumption Say we want to make ctrl+n to save the current file. In VSCode, you put this in keybindings.json: [ { "key": "ctrl+n", "command": "workbench.action.files.save" } ] In Neovim, you put this in init.lua: vim.keymap.set('n', '<c-n>', function() vim.cmd.write() end) Now, a couple of differences to see. First, the VSCode example is invoking a fixed, built-in command, while Neovim can bind an arbitrary function. Second, in VSCode you edit a static configuration file with static data, while in Neovim you execute a command that edits the running editor state. In fact, it doesn't even need to be in a configuration file: you can add a new keymap directly from the command line. Though you'd probably instead do that command in the OG Vim way: map <c-n> :w<CR> And that does something different than a function: it makes pressing ctrl+n mean "d

## Logic for Programmers is Done

DevFeed: [Logic for Programmers is Done](<https://devfeed.tech/articles/logic-for-programmers-is-done-25491.md>)

Original publisher: [Read original article](<https://buttondown.com/hillelwayne/archive/logic-for-programmers-is-done/>)

Author: Hillel Wayne

Published: 2026-07-29T15:43:52Z

Content type: release

Language: en

Sources: [Newsletter feed for Hillel Wayne's Newsletter](<https://devfeed.tech/sources/newsletter-feed-for-hillel-wayne-s-newsletter.md>)

Topics: [Publishing](<https://devfeed.tech/topics/publishing.md>), [LaTeX](<https://devfeed.tech/topics/latex.md>)

Tags: [announcement](<https://devfeed.tech/tags/announcement.md>), [blog](<https://devfeed.tech/tags/blog.md>), [book](<https://devfeed.tech/tags/book.md>), [free](<https://devfeed.tech/tags/free.md>), [latex](<https://devfeed.tech/tags/latex.md>), [typography](<https://devfeed.tech/tags/typography.md>)

### AI overview

Logic for Programmers has reached version 1.0 and is available in print. Early ebook buyers can download the completed edition for free through Leanpub.

### Source excerpt

It's done. Logic for Programmers is now 1.0 and available in print. Full announcement here, official website here, direct Amazon link here. If you got an early version of the ebook, you can go back to leanpub to download the 1.0 for free. This marks the completion of a project that took five years of work, six bookwriting professionals, fourteen domain experts, and fifteen public alphas. This has been, without a doubt, the biggest and most exhausting project I've ever done. The examples in the discarded drafts alone could make a second book. The cursed knowledge I've gained on LaTeX and typography could fill a third (or at least a couple of entertaining blog posts). Self-publishing was simultaneously the worst and best decision I made. Now excuse me I am going to sleep for a month.

## Logic for Programmers v0.15, Livecoding

DevFeed: [Logic for Programmers v0.15, Livecoding](<https://devfeed.tech/articles/logic-for-programmers-v0-15-livecoding-25493.md>)

Original publisher: [Read original article](<https://buttondown.com/hillelwayne/archive/logic-for-programmers-v015-livecoding/>)

Author: Hillel Wayne

Published: 2026-06-17T16:40:09Z

Content type: release

Language: en

Sources: [Newsletter feed for Hillel Wayne's Newsletter](<https://devfeed.tech/sources/newsletter-feed-for-hillel-wayne-s-newsletter.md>)

Topics: [releases](<https://devfeed.tech/topics/releases.md>), [Release notes](<https://devfeed.tech/topics/release-notes.md>), [JavaScript](<https://devfeed.tech/topics/javascript.md>), [Haskell](<https://devfeed.tech/topics/haskell.md>)

Tags: [javascript](<https://devfeed.tech/tags/javascript.md>), [language](<https://devfeed.tech/tags/language.md>), [music](<https://devfeed.tech/tags/music.md>), [release](<https://devfeed.tech/tags/release.md>), [release-notes](<https://devfeed.tech/tags/release-notes.md>)

### AI overview

Logic for Programmers version 0.15 is described as the first true release candidate, with content complete and copy edited and proofread. The article also recommends the Strudel and CLAVIER-36 livecoding music languages discussed at a Chicago meetup.

### Source excerpt

There's a new release of Logic for Programmers! This one, version 0.15, is the first true release candidate. There's a couple of minor touch-ups I need to do but all content is in and copy edited and proofread. Unless something absolutely major comes up, the next release will be 1.0 and available in print. Full release notes here. (I'm also testing if a smaller low margin PDF is better for phones and computers. The printed book is going to have much larger proper margins. Let me know what you think about this experiment!) Anyway I hurt my wrist and can't write a whole newsletter, so I'll just do a couple of recommendations. My friend Richard Whaling invited me to the inaugural livecode.chicago meetup, which was pretty cool! Among other things, people demoed two languages for livecoding music: Strudel is an extensive javascript DSL that seems relatively friendly to get into CLAVIER-36 is a ground-up special purpose language where the program is a 2D grid It all kinda makes me wish I knew the basics of music because it seems like it'd be a lot of fun to compare and contrast all of the various approaches to livecoding languages. Check em out if that's at all your thing! (For completeness, Strudel is a port of the Haskell TidalCycles and C36 was inspired by Orca)

## Why JSON and other languages should allow more flexible separators

DevFeed: [Why JSON and other languages should allow more flexible separators](<https://devfeed.tech/articles/nontrailing-separators-do-not-spark-joy-25499.md>)

Original publisher: [Read original article](<https://buttondown.com/hillelwayne/archive/nontrailing-separators-do-not-spark-joy/>)

Author: Hillel Wayne

Published: 2026-06-10T12:22:04Z

Content type: opinion

Language: en

Sources: [Newsletter feed for Hillel Wayne's Newsletter](<https://devfeed.tech/sources/newsletter-feed-for-hillel-wayne-s-newsletter.md>)

Topics: [JSON](<https://devfeed.tech/topics/json.md>), [Haskell](<https://devfeed.tech/topics/haskell.md>), [Parsing](<https://devfeed.tech/topics/parsing.md>)

Tags: [json](<https://devfeed.tech/tags/json.md>), [language](<https://devfeed.tech/tags/language.md>), [standard](<https://devfeed.tech/tags/standard.md>), [transformation](<https://devfeed.tech/tags/transformation.md>)

### AI overview

The article argues that requiring separators in particular positions makes common code transformations harder. Using JSON as the main example, it compares trailing, leading, and both-sided separator styles across languages including Haskell, TLA+, Prolog, Python, Go, and Alloy.

### Source excerpt

This is valid JSON: { "a": 1, "b": 2, "c": 3 } This is invalid JSON: { "a": 1, "b": 2, "c": 3, } The difference is the last comma. The JSON grammar specifies that a comma can separate two members of an object but not postcede ("trail") a member. I think this was a design mistake. Say we want to add two new keys to the struct, one before the "a" member and one after the "c" member. Here's what it would look like if trailing commas were permitted: { + "x": 0, "a": 1, "b": 2, "c": 3, + "y": 4, } It's the exact same text transformation regardless of where we add the key. In the current model, we instead have this: { + "x": 0, "a": 1, "b": 2, - "c": 3 + "c": 3, + "y": 4 } Those are different transformations! Similarly if you want to remove an element, you can't just delete the corresponding line1, you have to delete the line and then check that the last line doesn't have a trailing comma. Don't even get me started on all the special cases involved in swapping two lines. JSON isn't the only language with this problem. Haskell writes record types like this: -- from https://play.haskell.org/ data Drone = Drone { xPos :: Int , yPos :: Int , zPos :: Int } This "partial bullet point" style of putting separators at the beginning of rows makes it easier to change the last row but harder to change the first one. TLA+ has this problem too: \* both valid VARIABLES a, b, c vars == <<a, b, c>> \* both invalid VARIABLES a, b, c, vars == <<a, b, c,>> This one's annoying because 1) you're constantly adding new top-level variables while working on a spec and 2) the PlusCal DSL does not have this problem: \* Totally fine! (*--algorithm foo { variables a; b; c; The worst offenders, IMO, are logic languages like Prolog. Not only don't you have trailing separators, you have a special terminating symbol: foo(A, B, C) :- A = 1, % comma B = 2, % comma C = 3. % period! I guess you can sort of think of it as funny-lookin' braces: foo(A, B, C) :- A = 1, B = 2, C = 3 . But this is not standard synt

## Logic for Programmers extra credits

DevFeed: [Logic for Programmers extra credits](<https://devfeed.tech/articles/logic-for-programmers-extra-credits-25490.md>)

Original publisher: [Read original article](<https://buttondown.com/hillelwayne/archive/logic-for-programmers-extra-credits/>)

Author: Hillel Wayne

Published: 2026-06-02T14:48:48Z

Content type: article

Language: en

Sources: [Newsletter feed for Hillel Wayne's Newsletter](<https://devfeed.tech/sources/newsletter-feed-for-hillel-wayne-s-newsletter.md>)

Topics: [Math and Logic](<https://devfeed.tech/topics/math-and-logic.md>), [Concurrent Programming](<https://devfeed.tech/topics/concurrent-programming.md>)

Tags: [book](<https://devfeed.tech/tags/book.md>), [concurrent](<https://devfeed.tech/tags/concurrent.md>), [math](<https://devfeed.tech/tags/math.md>), [writing](<https://devfeed.tech/tags/writing.md>)

### AI overview

The author announces four supplementary pieces for Logic for Programmers covering concurrent-process orderings, first-order logic and sets of functions, Liskov's history rule in subtyping, and orders on sets. The supplements contain roughly 2,000-3,000 words of math content and may be less polished than the book.

### Source excerpt

So I said there wasn't a proper newsletter this week, since I'm in Budapest prepping for a conference. But I still got a thing for y'all. There's a lot of interesting topics I wanted to cover for Logic for Programmers, but the book is dense enough as it is and many of these were too tangential or technical to fit in well. So I've been writing some supplements and uploading them here. I've got four so far: How we compute the number of orderings of multiple concurrent processes How first-order logic can quantify over "a set of functions", what a "set of functions" looks like, and how functions can be defined in terms of sets (plus a bit on currying and type theory) Barbara Liskov's "history rule" in subtyping Total and partial orders on sets. Now I'm going to caveat that these were written off the cuff and haven't gone through the obsessive editing of the book itself, so they may be rough and there might be errors in them. Still, it's like 2-3000 words of math content, so hopefully covers not having a proper newsletter this time. Seeya next week!

## Knowing about things is cheaper than knowing things

DevFeed: [Knowing about things is cheaper than knowing things](<https://devfeed.tech/articles/knowing-about-things-is-cheaper-than-knowing-things-25488.md>)

Original publisher: [Read original article](<https://buttondown.com/hillelwayne/archive/knowing-about-things-is-cheaper-than-knowing/>)

Author: Hillel Wayne

Published: 2026-05-28T16:03:01Z

Content type: article

Language: en

Sources: [Newsletter feed for Hillel Wayne's Newsletter](<https://devfeed.tech/sources/newsletter-feed-for-hillel-wayne-s-newsletter.md>)

Topics: [math](<https://devfeed.tech/topics/math.md>), [Programming](<https://devfeed.tech/topics/programming.md>), [Learning](<https://devfeed.tech/topics/learning.md>)

Tags: [learning](<https://devfeed.tech/tags/learning.md>), [math](<https://devfeed.tech/tags/math.md>), [programming](<https://devfeed.tech/tags/programming.md>), [writing](<https://devfeed.tech/tags/writing.md>)

### AI overview

The article argues that programmers benefit from broad exposure to many areas of mathematics and other knowledge, while only needing to study topics in depth when they are relevant to their domain. It distinguishes mathematics useful to all programmers from fields useful mainly to particular programmers, and recommends introductory resources and conference videos for broad learning.

### Source excerpt

Short one this week because I'm way behind on book and conference prep. Last week a LinkedIn Influencer wrote about how math has nothing to do with programming, so I spite-wrote a rejoinder about how math is necessary to program (just try to write software without knowing arithmetic!) and man I forgot how much spite can fuel writing. Maybe I should go back to Twitter (absolutely not). But it got me thinking about the difference between "all programmers can benefit from learning math" and "all programmers need to learn math". I simultaneously believe three things: There is some math, like arithmetic (incl. arithmetic of booleans, sets, functions, etc), that is useful to all programmers. The remaining fields aren't useful to most programmers. Every programmer works in a domain where there is at least one branch of math that would benefit them to learn. (2) means that if get a group of 100 software engineers and teach them something like algebra or calculus, you can't expect it to be applicable for more than 3 or 5. Whereas if you teach something like shell scripting or regular expressions it'd be useful to at least, like, 50. So no field of math has a good RoI for the average programmer. (3) means that each of those 100 developers could, on their own, find a field of math that is useful to them. In order to do that, though, they need to roughly know what the fields are, what the big ideas are, and where they might be useful. It is more useful to teach them about many fields than to teach them any one specific field in-depth. I think that's generally true with most areas of knowledge! Getting basic exposure to something takes a lot less time and effort than learning it in-depth. If you're specifically trying to learn things that will be useful to your work1, you only want to go in-depth on topics you know will be helpful. But you won't know the topic is helpful (or even that it exists) unless you know the very basics already. So it makes sense to get broad exposure to

## Assumptions weaken properties

DevFeed: [Assumptions weaken properties](<https://devfeed.tech/articles/assumptions-weaken-properties-25481.md>)

Original publisher: [Read original article](<https://buttondown.com/hillelwayne/archive/assumptions-weaken-properties/>)

Author: Hillel Wayne

Published: 2026-05-20T15:13:16Z

Content type: tutorial

Language: en

Sources: [Newsletter feed for Hillel Wayne's Newsletter](<https://devfeed.tech/sources/newsletter-feed-for-hillel-wayne-s-newsletter.md>)

Topics: [Formal methods](<https://devfeed.tech/topics/formal-methods.md>), [Math and Logic](<https://devfeed.tech/topics/math-and-logic.md>), [Parser](<https://devfeed.tech/topics/parser.md>), [JSON](<https://devfeed.tech/topics/json.md>)

Tags: [bug](<https://devfeed.tech/tags/bug.md>), [formal-methods](<https://devfeed.tech/tags/formal-methods.md>), [json](<https://devfeed.tech/tags/json.md>), [math](<https://devfeed.tech/tags/math.md>), [test](<https://devfeed.tech/tags/test.md>)

### AI overview

The article explains, using logical implication, why adding assumptions weakens a formal property. It illustrates the idea with tests, formal specifications, fairness constraints, and a JSON parser verified only for ASCII input.

### Source excerpt

In some tests are stronger than others, I defined STRONG => WEAK to mean "any system passing test STRONG is also guaranteed to pass WEAK". This uses the logical implication operator, defined as P => Q = !P || (P && Q). Implication may be the most overworked operator in logic. Among other things, it's also used in formal specification, where Spec => Prop means "any system satisfying Spec has property Prop" and ASSUME => Spec means "The assumption ASSUME must hold in order for the system to satisfy Spec." Now let's mush these all together and do some math. To start, "the system has property Prop" is the same as "the system passes the test that checks Prop", so test strength is also property strength. Now let "ASSUME => Prop" mean "the system passes Prop assuming ASSUME is true." In classic logic, if P is true, then obviously !Q || P is true. Further, that is equivalent (just draw the truth table!) to !Q || (P && Q). So for any propositions P and Q, P => (Q => P). In other words, Prop => (ASSUME => Prop). In other other words, "the system passes Prop" is a stronger property than "the system passes Prop whenever our assumptions hold." In other other other words, any assumption added makes a property weaker. This makes intuitive sense to me. A JSON parser that's only been verified with ASCII strings has the property "input only uses ASCII && is valid json => correctly parsed". A better JSON parser that works for all Unicode will have the property "is valid json => correctly parsed", which has fewer assumptions, meaning it's guaranteed to work in a strict superset of cases. It also matches the intuition that "more assumptions means more likely to go wrong". We have a bug whenever Prop is false. The only way for Spec => Prop to be true and Prop be false is if Spec is false, eg our system doesn't satisfy the specification we intended to implement. On the other hand, Spec => (ASSUME => Prop) && !Prop is true whenever Spec and/or ASSUME is false, meaning a correctly-implement

## Points are a weird and inconsistent unit of measure

DevFeed: [Points are a weird and inconsistent unit of measure](<https://devfeed.tech/articles/points-are-a-weird-and-inconsistent-unit-of-measure-25502.md>)

Original publisher: [Read original article](<https://buttondown.com/hillelwayne/archive/points-are-a-weird-and-inconsistent-unit-of/>)

Author: Hillel Wayne

Published: 2026-05-13T15:56:37Z

Content type: opinion

Language: en

Sources: [Newsletter feed for Hillel Wayne's Newsletter](<https://devfeed.tech/sources/newsletter-feed-for-hillel-wayne-s-newsletter.md>)

Topics: [LaTeX](<https://devfeed.tech/topics/latex.md>), [TeX](<https://devfeed.tech/topics/tex.md>)

Tags: [articles](<https://devfeed.tech/tags/articles.md>), [latex](<https://devfeed.tech/tags/latex.md>), [technology](<https://devfeed.tech/tags/technology.md>)

### AI overview

The author investigates why a 10.8pt x 7.2pt grid differs between LaTeX and Inkscape. The article explains that point definitions varied historically and that TeX uses a slightly different point size from the official printer's point.

### Source excerpt

I'm in the middle of redoing the Logic for Programmers diagrams and this has surfaced a really annoying problem. The book is formatted in LaTeX using a pseudo-grid of 10.8pt x 7.2pt. The diagrams are done in Inkscape using a 10.8pt x 7.2pt. Last week I found out that these are not the same points. Latex defines a point as 1/72.27 inches (0.3515 millimeters). Inkscape instead uses 1/72 inches (0.3528 mm). It's only a difference of 0.4% but it still floors me that two widespread digital technologies would be different! So, uh, what happened? A few hours of reading articles later, this is what I found, caveat that I didn't spend all that much time researching this and this is only initial impressions. what even is a point A point is a typographic measure, coming from 1517, that is supposedly the smallest interesting size for a printer. This was notably not a standardized measure- different companies in times used different point sizes depending on their equipment. Over time it was standardized, but each country picked a different standard: the German and Japanese point is 0.250 mm, the French point is allegedly 0.399 mm, etc. But early computer history is super Americentric so that's what technology uses. In the US, they standardized the point around the end of the 19th century. To what? I dunno. This source from 1900 gives the length of a point as 35/996 cm (72.281 points/in) and then says there are exactly 867.4699 "ems per foot" (72.289 points/in). This source from 1916 says the standard pica (12 points) is 0.16604 inches and that there are 72.272 "pica ems per foot". Which conveniently enough gives us 72.272 points/in (a pica being 12 points). Then on the very next page they say no a pica is actually 0.166044 inches and a point is exactly 0.013837 inches. I found other sources with other definitions, too. I'm going to chalk the differences up to a mix of "the definitions of 'meter' and 'foot' changed over time" and "these are less than a micron apart so who gives a

## New Logic for Programmers (and the future of this newsletter)

DevFeed: [New Logic for Programmers (and the future of this newsletter)](<https://devfeed.tech/articles/new-logic-for-programmers-and-the-future-of-this-newsletter-25497.md>)

Original publisher: [Read original article](<https://buttondown.com/hillelwayne/archive/new-logic-for-programmers-and-the-future-of-this/>)

Author: Hillel Wayne

Published: 2026-05-06T17:03:46Z

Content type: opinion

Language: en

Sources: [Newsletter feed for Hillel Wayne's Newsletter](<https://devfeed.tech/sources/newsletter-feed-for-hillel-wayne-s-newsletter.md>)

Topics: [Formal methods](<https://devfeed.tech/topics/formal-methods.md>), [Fuzzing/Fuzz testing](<https://devfeed.tech/topics/fuzzing.md>), [Testing](<https://devfeed.tech/topics/testing.md>), [Programming](<https://devfeed.tech/topics/programming.md>)

Tags: [developer](<https://devfeed.tech/tags/developer.md>), [formal-methods](<https://devfeed.tech/tags/formal-methods.md>), [fuzzing](<https://devfeed.tech/tags/fuzzing.md>), [newsletter](<https://devfeed.tech/tags/newsletter.md>), [release](<https://devfeed.tech/tags/release.md>), [testing](<https://devfeed.tech/tags/testing.md>), [updates](<https://devfeed.tech/tags/updates.md>)

### AI overview

The author announces version 0.14 of Logic for Programmers, reports progress toward a 1.0 print edition, and shares plans to join Antithesis as a developer educator. The newsletter may shift toward software history and related topics, with its future publishing frequency uncertain.

### Source excerpt

So first the immediate news: I just released version 0.14 of Logic for Programmers! This release is pretty similar to 0.13. There are a few rewrites but the vast majority of the changes are layout, copyediting, and technical editing. Full notes here. In related news, I've started doing test prints of the book: There's not a whole lot left to be done. I've gotta fix up some diagrams, do more formatting and proofreading, incorporate some fixes raised by readers, and make a website and back cover. After that, the book should be ready for 1.0. I'm aiming to have print copies purchasable by the end of June! Now the big news: starting August, I'll be a full-time employee of Antithesis, a generative testing platform. Officially my role is "developer educator", and I'll be tasked with making "property-based testing, fuzzing, fault injection, Hegel, Bombadil, and the Antithesis platform understandable to everyday engineers". So the same kind of work I do now, except with far more support and a matching 401(k). I already have three pages of topic ideas you have no idea how excited I am about this So how is this going to affect the newsletter? First, I want to make clear that this is not going to become an Antithesis newsletter. My Antithesis-related work is going to be on their official platforms. I do think one of the best ways to make a topic "understandable" is to write foundational material that's useful to all engineers, whether they're invested in the topic or not. I might share links to things I make along those lines, but they'll be just that, links. At the same time, the content of this newsletter will change a little. Property testing and fuzzing aren't the same as formal methods, but a lot of the foundations overlap, especially in how we think about properties and correctness. I don't know for sure yet, but I suspect that I'll start biasing this newsletter away from Antithesis related topics. So there will probably be less theoretic things like what does undecidabl

## Illegal vs Unwanted States

DevFeed: [Illegal vs Unwanted States](<https://devfeed.tech/articles/illegal-vs-unwanted-states-25485.md>)

Original publisher: [Read original article](<https://buttondown.com/hillelwayne/archive/illegal-vs-unwanted-states/>)

Author: Hillel Wayne

Published: 2026-04-28T15:14:09Z

Content type: article

Language: en

Sources: [Newsletter feed for Hillel Wayne's Newsletter](<https://devfeed.tech/sources/newsletter-feed-for-hillel-wayne-s-newsletter.md>)

Topics: [systems](<https://devfeed.tech/topics/systems.md>), [Software](<https://devfeed.tech/topics/software.md>), [data](<https://devfeed.tech/topics/data.md>), [Network](<https://devfeed.tech/topics/network.md>), [Server](<https://devfeed.tech/topics/server.md>)

Tags: [airline](<https://devfeed.tech/tags/airline.md>), [data](<https://devfeed.tech/tags/data.md>), [data-type](<https://devfeed.tech/tags/data-type.md>), [event](<https://devfeed.tech/tags/event.md>), [network](<https://devfeed.tech/tags/network.md>), [optional](<https://devfeed.tech/tags/optional.md>), [servers](<https://devfeed.tech/tags/servers.md>), [software](<https://devfeed.tech/tags/software.md>), [state](<https://devfeed.tech/tags/state.md>), [systems](<https://devfeed.tech/tags/systems.md>)

### AI overview

The article distinguishes illegal states, which a system must never enter, from unwanted states, which may be temporarily acceptable but must not persist or develop into illegal states. It argues that systems should represent unwanted states because external inputs and unreliable infrastructure make them unavoidable, while detection and resolution mechanisms limit their consequences.

### Source excerpt

An illegal state is a state we never want our system to be in. An unwanted state is a state we don't want to stay in. Many states that we wish were illegal are actually unwanted. Considering a calendaring software which stores calendar events as {user: {events: [event]}}, where each event has a start and end time. This allows one person to attend two events at the same time. We might consider this illegal and replace the data type with {user: {time: optional event}} which makes this impossible. However, a scheduling conflict isn't illegal, only unwanted! It is possible for a person to sign up for two overlapping events. Maybe they're supposed to choose one event, maybe they'll decide which event to go to later, maybe one of the events doesn't actually represent an in-person meeting. In that case it's acceptable, if not ideal, to remain in the unwanted state. Other unwanted states lead to invalid states if not exited quickly. An airline flight is in an unwanted state if there are more passengers booked to fly than seats available. This must be resolved before passengers actually board, as "more passengers physically on the plane than seats available" is an illegal state. In some cases, an unwanted state does not lead to illegal states, but permanently remaining in the unwanted state is still a problem. We might guarantee that a network partition does not ever lead to inconsistent data. Even though the unwanted state of a network partition cannot cause the illegal state of corrupt data, we still have a big problem if we don't ever fix the partition. Why systems must represent unwanted states Generally, unwanted states can happen if we don't have complete control over our system's behavior. We can't guarantee our network is perfectly reliable, our servers are always up, our users all put in correct data. If our system gets input from the external world then the world can push us into an unwanted state. We need to be able to detect these states so we can resolve them. E

## A sufficiently comprehensive spec is not (necessarily) code

DevFeed: [A sufficiently comprehensive spec is not (necessarily) code](<https://devfeed.tech/articles/a-sufficiently-comprehensive-spec-is-not-necessarily-code-25479.md>)

Original publisher: [Read original article](<https://buttondown.com/hillelwayne/archive/a-sufficiently-comprehensive-spec-is-not/>)

Author: Hillel Wayne

Published: 2026-04-15T16:18:02Z

Content type: opinion

Language: en

Sources: [Newsletter feed for Hillel Wayne's Newsletter](<https://devfeed.tech/sources/newsletter-feed-for-hillel-wayne-s-newsletter.md>)

Topics: [Specifications](<https://devfeed.tech/topics/specifications.md>), [implementation](<https://devfeed.tech/topics/implementation.md>), [Claude Code](<https://devfeed.tech/topics/claude-code.md>), [Code](<https://devfeed.tech/topics/code.md>), [Large Language Model](<https://devfeed.tech/topics/llm.md>)

Tags: [claude-code](<https://devfeed.tech/tags/claude-code.md>), [code](<https://devfeed.tech/tags/code.md>), [implementation](<https://devfeed.tech/tags/implementation.md>), [llms](<https://devfeed.tech/tags/llms.md>), [specifications](<https://devfeed.tech/tags/specifications.md>)

### AI overview

The article argues that a sufficiently comprehensive specification is not necessarily code. A specification describes a set of possible implementations, while code is one implementation within that set; even specifications used with Claude Code can leave important design decisions unresolved.

### Source excerpt

Sorry for missing last week! Was sick and then busy. This week I want to cover a pet peeve of mine, best seen in this comic: A "comprehensive and precise spec" is not necessarily code. A specification corresponds to a set of possible implementations, and code is a single implementation in that set. As long as the set has more than one element, there is a separation between the spec and the code. Consider a business person (bp) who asks: I want a tool to convert miles to kilometers. Is this a comprehensive spec? Maybe, you can give it to Claude Code and tell it to make all design decisions and it will give you a program that converts miles to km. At the same time, there is a huge amount of details left out of this. What language? What's the UX? Should it be a command line script or a mobile app or an enterprise SaaS? For this reason, if we gave Claude's output to the bp, they'll probably be unsatisfied. The set of possible implementations includes the programs they want, but also lots of programs they don't want. So they now they say: It should be a textbox on a website. Okay, this rules out a lot more stuff, but there's still a lot to decide. React or vanillajs or htmx? Should the output be a separate textbox or a popup? Should we use a conversion of 1.6, 1.61, or 1.609? So you could argue that this is still not a "comprehensive and precise spec". But what if the bp is happy with whatever Claude makes? Then their spec was sufficiently comprehensive and precise, since they got a program that solved their problem! Now the comic above makes the more specific claim that a spec "comprehensive and precise enough to generate a program" is code. That wasn't even true before LLMs. Program synthesis, the automatic generation of conformant programs from specifications, is an active field of research! Last I checked in 2019 they were only generating local functions from type specifications; I don't know how things have changed with LLMs. But still, it shows that code and compre

## Choose Boring Technology and Innovative Practices

DevFeed: [Choose Boring Technology and Innovative Practices](<https://devfeed.tech/articles/choose-boring-technology-and-innovative-practices-25482.md>)

Original publisher: [Read original article](<https://buttondown.com/hillelwayne/archive/choose-boring-technology-and-innovative-practices/>)

Author: Hillel Wayne

Published: 2026-03-24T14:38:06Z

Content type: opinion

Language: en

Sources: [Newsletter feed for Hillel Wayne's Newsletter](<https://devfeed.tech/sources/newsletter-feed-for-hillel-wayne-s-newsletter.md>)

Topics: [maintenance](<https://devfeed.tech/topics/maintenance.md>), [legacy](<https://devfeed.tech/topics/legacy.md>), [Software](<https://devfeed.tech/topics/software.md>), [The Julia Language](<https://devfeed.tech/topics/julia.md>), [Databases](<https://devfeed.tech/topics/databases.md>)

Tags: [innovation](<https://devfeed.tech/tags/innovation.md>), [legacy](<https://devfeed.tech/tags/legacy.md>), [maintenance](<https://devfeed.tech/tags/maintenance.md>), [technology](<https://devfeed.tech/tags/technology.md>), [time](<https://devfeed.tech/tags/time.md>)

### AI overview

The article argues that innovative technology carries unknown risks and long-term maintenance costs, while practices can be changed more easily. It recommends being conservative about business-critical software and more willing to innovate in development practices and tools.

### Source excerpt

The famous article Choose Boring Technology lists two problems with using innovative technology: There are too many "unknown unknowns" in a new technology, whereas in boring technology the pitfalls are already well-known. Shiny tech has a maintenance burden that persist long after everybody has gotten bored with it. Both of these tie back to the idea that the main cost of technology is maintenance. Even if something is easy to build with, it might not be as easy to keep running. We cannot "abandon" mission-critical technology. Say my team builds a new service on Julia, and 2 years later decides it was the wrong choice. We're stuck with either the (expensive) process of migrating all our data to Postgres code to Java or the (expensive) process of keeping it running anyway. Either way, the company needs to spend resources keeping engineers trained on the tech instead of other useful things, like how to mine crypto in their heads. Tech is slow to change. Not as slow to change as, say, a bridge, but still pretty slow. Now say at the same time as Julia, we also decided to start practicing test && commit || revert (TCR). After two years, we get sick of that, too. To deal with this, we can simply... not do TCR anymore. There is no "legacy practice" we need to support, no maintenance burden to dropping a process. It is much easier to adopt and abandon practices than it is to adopt and abandon technology. This means while we should be conservative in the software we use, we can be more freely innovative in how we use it. If we get three innovation tokens for technology, we get like six or seven for practices. And we can trade in our practices to get those tokens back. (The flip side of this is that social processes are less "stable" than technology and take more work to keep running. This is why "engineering controls" are considered more effective as reducing accidents than administrative controls.) Choose Boring Material and Innovative Tools Pushing this argument further, w

## LLMs are bad at vibing specifications

DevFeed: [LLMs are bad at vibing specifications](<https://devfeed.tech/articles/llms-are-bad-at-vibing-specifications-25489.md>)

Original publisher: [Read original article](<https://buttondown.com/hillelwayne/archive/llms-are-bad-at-vibing-specifications/>)

Author: Hillel Wayne

Published: 2026-03-10T17:12:30Z

Content type: article

Language: en

Sources: [Newsletter feed for Hillel Wayne's Newsletter](<https://devfeed.tech/sources/newsletter-feed-for-hillel-wayne-s-newsletter.md>)

Topics: [Formal methods](<https://devfeed.tech/topics/formal-methods.md>), [Large Language Model](<https://devfeed.tech/topics/llm.md>), [Specifications](<https://devfeed.tech/topics/specifications.md>), [Artificial Intelligence](<https://devfeed.tech/topics/ai.md>)

Tags: [ai](<https://devfeed.tech/tags/ai.md>), [formal-methods](<https://devfeed.tech/tags/formal-methods.md>), [llms](<https://devfeed.tech/tags/llms.md>), [nondeterminism](<https://devfeed.tech/tags/nondeterminism.md>), [specifications](<https://devfeed.tech/tags/specifications.md>), [verification](<https://devfeed.tech/tags/verification.md>)

### AI overview

The article examines AI-generated TLA+ and Alloy specifications through a case study. It argues that these specifications may fail to compile or model-check and often contain tautological or obvious properties rather than subtle properties involving concurrency, nondeterminism, or multi-step bad behavior.

### Source excerpt

No newsletter next week I'll be speaking at InfoQ London. But see below for a book giveaway! LLMs are bad at vibing specifications About a year ago I wrote AI is a gamechanger for TLA+ users, which argued that AI are a "specification force multiplier". That was written from the perspective an TLA+ expert using these tools. A full 4% of Github TLA+ specs now have the word "Claude" somewhere in them. This is interesting to me, because it suggests there was always an interest in formal methods, people just lacked the skills to do it. It's also interesting because it gives me a sense of what happens when beginners use AI to write formal specs. It's not good. As a case study, we'll use this project, which is kind of enough to have vibed out TLA+ and Alloy specs. Looking at a project Starting with the Alloy spec. Here it is in its entirety: module ThreatIntelMesh sig Node {} one sig LocalNode extends Node {} sig Snapshot { owner: one Node, signed: one Bool, signatures: set Signature } sig Signature {} sig Policy { allowUnsignedImport: one Bool } pred canImport[p: Policy, s: Snapshot] { (p.allowUnsignedImport = True) or (s.signed = True) } assert UnsignedImportMustBeDenied { all p: Policy, s: Snapshot | p.allowUnsignedImport = False and s.signed = False implies not canImport[p, s] } assert SignedImportMayBeAccepted { all p: Policy, s: Snapshot | s.signed = True implies canImport[p, s] } check UnsignedImportMustBeDenied for 5 check SignedImportMayBeAccepted for 5 Couple of things to note here: first of all, this doesn't actually compile. It's using the Boolean standard module so needs open util/boolean to function. Second, Boolean is the wrong approach here; you're supposed to use subtyping. sig Snapshot { owner: one Node, - signed: one Bool, signatures: set Signature } + sig SignedSnapshot in Snapshot {} pred canImport[p: Policy, s: Snapshot] { - s.signed = True + s in SignedSnapshot } So we know the person did not actually run these specs. This is somewhat less of a probl

## Free Books

DevFeed: [Free Books](<https://devfeed.tech/articles/free-books-25483.md>)

Original publisher: [Read original article](<https://buttondown.com/hillelwayne/archive/free-books/>)

Author: Hillel Wayne

Published: 2026-03-03T16:34:33Z

Content type: news

Language: en

Sources: [Newsletter feed for Hillel Wayne's Newsletter](<https://devfeed.tech/sources/newsletter-feed-for-hillel-wayne-s-newsletter.md>)

Topics: [Math and Logic](<https://devfeed.tech/topics/math-and-logic.md>), [bug](<https://devfeed.tech/topics/bug.md>)

Tags: [books](<https://devfeed.tech/tags/books.md>), [bug](<https://devfeed.tech/tags/bug.md>), [europe](<https://devfeed.tech/tags/europe.md>), [free](<https://devfeed.tech/tags/free.md>)

### AI overview

The newsletter is skipped for the week, with ten free copies of Logic for Programmers offered as an apology. The giveaway timing was intended to give people in Europe a better chance to claim copies, but a Leanpub bug prevented the release from working properly.

### Source excerpt

Spinning a lot of plates this week so skipping the newsletter. As an apology, have ten free copies of Logic for Programmers. These five are available now. These five should be available at 10:30 AM CEST tomorrow, so people in Europe have a better chance of nabbing one. Nevermind Leanpub had a bug that made this not work properly

## New Blog Post: Some Silly Z3 Scripts I Wrote

DevFeed: [New Blog Post: Some Silly Z3 Scripts I Wrote](<https://devfeed.tech/articles/new-blog-post-some-silly-z3-scripts-i-wrote-25496.md>)

Original publisher: [Read original article](<https://buttondown.com/hillelwayne/archive/new-blog-post-some-silly-z3-scripts-i-wrote/>)

Author: Hillel Wayne

Published: 2026-02-23T16:49:10Z

Content type: article

Language: en

Sources: [Newsletter feed for Hillel Wayne's Newsletter](<https://devfeed.tech/sources/newsletter-feed-for-hillel-wayne-s-newsletter.md>)

Topics: [Code](<https://devfeed.tech/topics/code.md>), [Math and Logic](<https://devfeed.tech/topics/math-and-logic.md>), [Script](<https://devfeed.tech/topics/script.md>)

Tags: [blog-post](<https://devfeed.tech/tags/blog-post.md>), [code](<https://devfeed.tech/tags/code.md>), [idea](<https://devfeed.tech/tags/idea.md>)

### AI overview

The author announces a new blog post containing Z3 scripts and discusses its mathematical examples, including total operations, arrays, quantifiers, and an unsuccessful attempt to encode Goldbach's conjecture as an SMT problem.

### Source excerpt

Now that I'm not spending all my time on Logic for Programmers, I have time to update my website again! So here's the first blog post in five months: Some Silly Z3 Scripts I Wrote. Normally I'd also put a link to the Patreon notes but I've decided I don't like publishing gated content and am going to wind that whole thing down. So some quick notes about this post: Part of the point is admittedly to hype up the eventual release of LfP. I want to start marketing the book, but don't want the marketing material to be devoid of interest, so tangentially-related-but-independent blog posts are a good place to start. The post discusses the concept of "chaff", the enormous quantity of material (both code samples and prose) that didn't make it into the book. The book is about 50,000 words... and considerably shorter than the total volume of chaff! I don't think most of it can be turned into useful public posts, but I'm not entirely opposed to the idea. Maybe some of the old chapters could be made into something? Coming up with a conditioned mathematical property to prove was a struggle. I had two candidates: a == b * c => a / b == c, which would have required a long tangent on how division must be total in Z3, and a != 0 => some b: b * a == 1, which would have required introducing a quantifier (SMT is real weird about quantifiers). Division by zero has already caused me enough grief so I went with the latter. This did mean I had to reintroduce "operations must be total" when talking about arrays. I have no idea why the array example returns 2 for the max profit and not 99999999. I'm guessing there's some short circuiting logic in the optimizer when the problem is ill-defined? One example I could not get working, which is unfortunate, was a demonstration of how SMT solvers are undecidable via encoding Goldbach's conjecture as an SMT problem. Anything with multiple nested quantifiers is a pain.

## Stream of Consciousness Driven Development

DevFeed: [Stream of Consciousness Driven Development](<https://devfeed.tech/articles/stream-of-consciousness-driven-development-25506.md>)

Original publisher: [Read original article](<https://buttondown.com/hillelwayne/archive/stream-of-consciousness-driven-development/>)

Author: Hillel Wayne

Published: 2026-02-18T16:33:08Z

Content type: opinion

Language: en

Sources: [Newsletter feed for Hillel Wayne's Newsletter](<https://devfeed.tech/sources/newsletter-feed-for-hillel-wayne-s-newsletter.md>)

Topics: [Development](<https://devfeed.tech/topics/development.md>), [Specifications](<https://devfeed.tech/topics/specifications.md>), [Markdown](<https://devfeed.tech/topics/markdown.md>)

Tags: [concepts](<https://devfeed.tech/tags/concepts.md>), [development](<https://devfeed.tech/tags/development.md>), [experience](<https://devfeed.tech/tags/experience.md>), [markdown](<https://devfeed.tech/tags/markdown.md>), [pairing](<https://devfeed.tech/tags/pairing.md>), [specifications](<https://devfeed.tech/tags/specifications.md>)

### AI overview

The author describes a pairing technique for conceptually complex specification work: write the problem, discussion, rejected approaches, proposed solution, and rationale in a Markdown file before changing the specification. This helps partners understand the reasoning and agree on an approach, though the author reports having tried it only once.

### Source excerpt

This is something I just tried out last week but it seems to have enough potential to be worth showing unpolished. I was pairing with a client on writing a spec. I saw a problem with the spec, a convoluted way of fixing the spec. Instead of trying to verbally explain it, I started by creating a new markdown file: NameOfProblem.md Then I started typing. First the problem summary, then a detailed description, then the solution and why it worked. When my partner asked questions, I incorporated his question and our discussion of it into the flow. If we hit a dead end with the solution, we marked it out as a dead end. Eventually the file looked something like this: Current state of spec Problems caused by this Elaboration of problems What we tried that didn't work Proposed Solution Theory behind proposed solution How the solution works Expected changes Other problems this helps solve Problems this does *not* help with Only once this was done, my partner fully understood the chain of thought, and we agreed it represented the right approach, did we start making changes to the spec. How is this better than just making the change? The change was conceptually complex. A rough analogy: imagine pairing with a beginner who wrote an insertion sort, and you want to replace it with quicksort. You need to explain why the insertion sort is too slow, why the quicksort isn't slow, and how quicksort actually correctly sorts a list. This could involve tangents into computational complexity, big-o notation, recursion, etc. These are all concepts you have internalized, so the change is simple to you, but the solution uses concepts the beginner does not know. So it's conceptually complex to them. I wasn't pairing with a beginning programmer or even a beginning specifier. This was a client who could confidently write complex specs on their own. But they don't work on specifications full time like I do. Any time there's a relative gap in experience in a pair, there's solutions that are concep

## Proving What's Possible

DevFeed: [Proving What's Possible](<https://devfeed.tech/articles/proving-what-s-possible-25503.md>)

Original publisher: [Read original article](<https://buttondown.com/hillelwayne/archive/proving-whats-possible/>)

Author: Hillel Wayne

Published: 2026-02-11T18:36:53Z

Content type: opinion

Language: en

Sources: [Newsletter feed for Hillel Wayne's Newsletter](<https://devfeed.tech/sources/newsletter-feed-for-hillel-wayne-s-newsletter.md>)

Topics: [Formal methods](<https://devfeed.tech/topics/formal-methods.md>), [Specifications](<https://devfeed.tech/topics/specifications.md>), [Math and Logic](<https://devfeed.tech/topics/math-and-logic.md>), [systems](<https://devfeed.tech/topics/systems.md>)

Tags: [flow](<https://devfeed.tech/tags/flow.md>), [formal-methods](<https://devfeed.tech/tags/formal-methods.md>), [specifications](<https://devfeed.tech/tags/specifications.md>), [state](<https://devfeed.tech/tags/state.md>), [statement](<https://devfeed.tech/tags/statement.md>)

### AI overview

A formal methods consultant introduces possibility properties for reasoning about what can happen in a system, distinguishing them from safety and liveness properties. Using temporal-logic notation and examples involving databases and state machines, the article describes possibility and reachability properties and combinations such as always possible and eventually possible.

### Source excerpt

As a formal methods consultant I have to mathematically express properties of systems. I generally do this with two "temporal operators": A(x) means that x is always true. For example, a database table always satisfies all record-level constraints, and a state machine always makes valid transitions between states. If x is a statement about an individual state (as in the database but not state machine example), we further call it an invariant. E(x) means that x is "eventually" true, conventionally meaning "guaranteed true at some point in the future". A database transaction eventually completes or rolls back, a state machine eventually reaches the "done" state, etc. These come from linear temporal logic, which is the mainstream notation for expressing system properties. 1 We like these operators because they elegantly cover safety and liveness properties, and because we can combine them. A(E(x)) means x is true an infinite number of times, while A(x => E(y) means that x being true guarantees y true in the future. There's a third class of properties, that I will call possibility properties: P(x) is "can x happen in this model"? Is it possible for a table to have more than ten records? Can a state machine transition from "Done" to "Retry", even if it doesn't? Importantly, P(x) does not need to be possible immediately, just at some point in the future. It's possible to lose 100 dollars betting on slot machines, even if you only bet one dollar at a time. If x is a statement about an individual state, we can further call it a reachability property. I'm going to use the two interchangeably for flow. A(P(x)) says that x is always possible. No matter what we've done in our system, we can make x happen again. There's no way to do this with just A and E. Other meaningful combinations include: P(A(x)): there is a reachable state from which x is always true. A(x => P(y)): y is possible from any state where x is true. E(x && P(y)): There is always a future state where x is true a

## Logic for Programmers New Release and Next Steps

DevFeed: [Logic for Programmers New Release and Next Steps](<https://devfeed.tech/articles/logic-for-programmers-new-release-and-next-steps-25492.md>)

Original publisher: [Read original article](<https://buttondown.com/hillelwayne/archive/logic-for-programmers-new-release-and-next-steps/>)

Author: Hillel Wayne

Published: 2026-02-04T14:00:00Z

Content type: release

Language: en

Sources: [Newsletter feed for Hillel Wayne's Newsletter](<https://devfeed.tech/sources/newsletter-feed-for-hillel-wayne-s-newsletter.md>)

Topics: [Release notes](<https://devfeed.tech/topics/release-notes.md>), [releases](<https://devfeed.tech/topics/releases.md>), [Programming](<https://devfeed.tech/topics/programming.md>)

Tags: [book](<https://devfeed.tech/tags/book.md>), [programming](<https://devfeed.tech/tags/programming.md>), [release](<https://devfeed.tech/tags/release.md>), [release-notes](<https://devfeed.tech/tags/release-notes.md>)

### AI overview

The article announces Logic for Programmers v0.13, a release exceeding 50,000 words and described as 20% larger than v0.12. It outlines rewritten chapters, a shift in the Alloy chapter toward domain modeling, expanded coverage of compatibility, testing, proofs, and answer set programming, and plans for copy editing, technical review, proofreading, and layout work.

### Source excerpt

It's taken four months, but the next release of Logic for Programmers is now available! v0.13 is over 50,000 words, making it both 20% larger than v0.12 and officially the longest thing I have ever written.1 Full release notes are here, but I'll talk a bit about the biggest changes. For one, every chapter has been rewritten. Every single one. They span from relatively minor changes to complete chapter rewrites. After some rough git diffing, I think I deleted about 11,000 words?2 The biggest change is probably to the Alloy chapter. After many sleepless nights, I realized the right approach wasn't to teach Alloy as a data modeling tool but to teach it as a domain modeling tool. Which technically means the book no longer covers data modeling. There's also a lot more connections between the chapters. The introductory math chapter, for example, foreshadows how each bit of math will be used in the future techniques. I also put more emphasis on the general "themes" like the expressiveness-guarantees tradeoff (working title). One theme I'm really excited about is compatibility (extremely working title). It turns out that the Liskov substitution principle/subtyping in general, database migrations, backwards-compatible API changes, and specification refinement all follow basically the same general principles. I'm calling this "compatibility" for now but prolly need a better name. Finally, there's just a lot more new topics in the various chapters. Testing properly covers structural and metamorphic properties. Proofs covers proof by induction and proving recursive functions (in an exercise). Logic Programming now finally has a section on answer set programming. You get the picture. Next Steps There's a lot I still want to add to the book: proper data modeling, data structures, type theory, model-based testing, etc. But I've added new material for two year, and if I keep going it will never get done. So with this release, all the content is in! Just like all the content was in

## Refining SQL Data Models Without Breaking Legacy Code

DevFeed: [Refining SQL Data Models Without Breaking Legacy Code](<https://devfeed.tech/articles/refinement-without-specification-25504.md>)

Original publisher: [Read original article](<https://buttondown.com/hillelwayne/archive/refinement-without-specification/>)

Author: Hillel Wayne

Published: 2026-01-20T17:49:07Z

Content type: article

Language: en

Sources: [Newsletter feed for Hillel Wayne's Newsletter](<https://devfeed.tech/sources/newsletter-feed-for-hillel-wayne-s-newsletter.md>)

Topics: [SQL](<https://devfeed.tech/topics/sql.md>), [Databases](<https://devfeed.tech/topics/databases.md>), [legacy](<https://devfeed.tech/topics/legacy.md>)

Tags: [database](<https://devfeed.tech/tags/database.md>), [event-sourcing](<https://devfeed.tech/tags/event-sourcing.md>), [legacy-code](<https://devfeed.tech/tags/legacy-code.md>), [sql](<https://devfeed.tech/tags/sql.md>), [sql-database](<https://devfeed.tech/tags/sql-database.md>)

### AI overview

This article explains how refinement mappings can support SQL schema changes while preserving compatibility with existing code. It applies the approach to migrating a boolean column to a timestamp, then to an event-sourcing-like model, and examines how mutability constraints affect whether the refinement remains valid.

### Source excerpt

Imagine we have a SQL database with a user table, and users have a non-nullable is_activated boolean column. Having read That Boolean Should Probably Be Something else, you decide to migrate it to a nullable activated_at column. You can change any of the SQL queries that read/update the user table but not any of the code that uses the results of these queries. Can we make this change in a way that preserves all external properties? Yes. If an update would set is_activated to true, instead set it to the current date. Now define the refinement mapping that takes a new_user and returns an old_user. All columns will be unchanged except is_activated, which will be f(new_user).is_activated = if new_user.activated_at == NULL then FALSE else TRUE Now new code can use new_user directly while legacy code can use f(new_user) instead, which will behave indistinguishably from the old_user. A little more time passes and you decide to switch to an event sourcing-like model. So instead of an activated_at column, you have a user_events table, where every record is (user_id, timestamp, event). So adding an activate event will activate the user, adding a deactivate event will deactivate the user. Once again, we can update the queries but not any of the code that uses the results of these queries. Can we make a change that preserves all external properties? Yes. If an update would change is_activated, instead have it add an appropriate record to the event table. Now, define the refinement mapping that takes newer_user and returns new_user. The activated_at field will be computed like this: g(newer_user).activated_at = # last_activated_event let lae = newer_user.events .filter(event = "activate" | "deactivate") .last, in if lae.event == "activate" then lae.timestamp else NULL Now new code can use newer_user directly while old code can use g(newer_user) and the really old code can use f(g(newer_user)). Mutability constraints I said "these preserve all external properties" and that was a

## My Gripes with Prolog

DevFeed: [My Gripes with Prolog](<https://devfeed.tech/articles/my-gripes-with-prolog-25495.md>)

Original publisher: [Read original article](<https://buttondown.com/hillelwayne/archive/my-gripes-with-prolog/>)

Author: Hillel Wayne

Published: 2026-01-14T16:48:51Z

Content type: opinion

Language: en

Sources: [Newsletter feed for Hillel Wayne's Newsletter](<https://devfeed.tech/sources/newsletter-feed-for-hillel-wayne-s-newsletter.md>)

Topics: [Programming](<https://devfeed.tech/topics/programming.md>), [Code](<https://devfeed.tech/topics/code.md>)

Tags: [code](<https://devfeed.tech/tags/code.md>), [language](<https://devfeed.tech/tags/language.md>), [programming](<https://devfeed.tech/tags/programming.md>)

### AI overview

An opinion article outlines the author's frustrations with Prolog while preparing sections on Answer Set Programming and Constraint Logic Programming. It discusses incompatible string implementations, the absence of functions, limited collection types, and the lack of boolean values, while acknowledging Prolog's bidirectionality as a useful feature.

### Source excerpt

For the next release of Logic for Programmers, I'm finally adding the sections on Answer Set Programming and Constraint Logic Programming that I TODOd back in version 0.9. And this is making me re-experience some of my pain points with Prolog, which I will gripe about now. If you want to know more about why Prolog is cool instead, go here or here or here or here. No standardized strings ISO "strings" are just atoms or lists of single-character atoms (or lists of integer character codes). The various implementations of Prolog add custom string operators but they are not cross compatible, so code written with strings in SWI-Prolog will not work in Scryer Prolog. No functions Code logic is expressed entirely in rules, predicates which return true or false for certain values. For example if you wanted to get the length of a Prolog list, you write this: ?- length([a, b, c], Len). Len = 3. Now this is pretty cool in that it allows bidirectionality, or running predicates "in reverse". To generate lists of length 3, you can write length(L, 3). But it also means that if you want to get the length a list plus one, you can't do that in one expression, you have to write length(List, Out), X is Out+1. For a while I thought no functions was necessary evil for bidirectionality, but then I discovered Picat has functions and works just fine. That by itself is a reason for me to prefer Picat for my LP needs. (Bidirectionality is a killer feature of Prolog, so it's a shame I so rarely run into situations that use it.) No standardized collection types besides lists Aside from atoms (abc) and numbers, there are two data types: Linked lists like [a,b,c,d]. Compound terms like dog(rex, poodle), which seem like record types but are actually tuples. You can even convert compound terms to linked lists with =..: ?- L =.. [a, b, c]. L = a(b, c). ?- a(b, c(c)) =.. L. L = [a, b, c(c)]. There's no proper key-value maps or even struct types. Again, this is something that individual distributions c

## The Liskov Substitution Principle: Subtyping Contracts, Invariants, and History Properties

DevFeed: [The Liskov Substitution Principle: Subtyping Contracts, Invariants, and History Properties](<https://devfeed.tech/articles/the-liskov-substitution-principle-does-more-than-you-think-25507.md>)

Original publisher: [Read original article](<https://buttondown.com/hillelwayne/archive/the-liskov-substitution-principle-does-more-than/>)

Author: Hillel Wayne

Published: 2026-01-06T16:51:26Z

Content type: article

Language: en

Sources: [Newsletter feed for Hillel Wayne's Newsletter](<https://devfeed.tech/sources/newsletter-feed-for-hillel-wayne-s-newsletter.md>)

Topics: [Object-oriented programming (OOP)](<https://devfeed.tech/topics/oop.md>), [Polymorphism](<https://devfeed.tech/topics/polymorphism.md>)

Tags: [inheritance](<https://devfeed.tech/tags/inheritance.md>), [oop](<https://devfeed.tech/tags/oop.md>), [properties](<https://devfeed.tech/tags/properties.md>), [solid-principles](<https://devfeed.tech/tags/solid-principles.md>), [subtype](<https://devfeed.tech/tags/subtype.md>)

### AI overview

The article explains that the Liskov Substitution Principle is broader than simply treating it as subtyping. It presents requirements involving method preconditions and postconditions, supertype state invariants, and history properties.

### Source excerpt

Happy New Year! I'm done with the newsletter hiatus and am going to try updating weekly again. To ease into things a bit, I'll try to keep posts a little more off the cuff and casual for a while, at least until Logic for Programmers is done. Speaking of which, v0.13 should be out by the end of this month. So for this newsletter I want to talk about the Liskov Substitution Principle (LSP). Last week I read A SOLID Load of Bull by cryptographer Loupe Vaillant, where he argues the SOLID principles of OOP are not worth following. He makes an exception for LSP, but also claims that it's "just subtyping" and further: If I were trying really hard to be negative about the Liskov substitution principle, I would stress that it only applies when inheritance is involved, and inheritance is strongly discouraged anyway. LSP is more interesting than that! In the original paper, A Behavioral Notion of Subtyping, Barbara Liskov and Jeannette Wing start by defining a "correct" subtyping as follows: Subtype Requirement: Let ϕ(x) be a property provable about objects x of type T. Then ϕ(y) should be true for objects y of type S where S is a subtype of T. From then on, the paper determine what guarantees that a subtype is correct.1 They identify three conditions: Each of the subtype's methods has the same or weaker preconditions and the same or stronger postconditions as the corresponding supertype method.2 The subtype satisfies all state invariants of the supertype. The subtype satisfies all "history properties" of the supertype. 3 e.g. if a supertype has an immutable field, the subtype cannot make it mutable. (Later, Elisa Baniassad and Alexander Summers would realize these are equivalent to "the subtype passes all black-box tests designed for the supertype", which I wrote a little bit more about here.) I want to focus on the first rule about preconditions and postconditions. This refers to the method's contract. For a function f, f.Pre is what must be true going into the function, and

## Some Fun Software Facts

DevFeed: [Some Fun Software Facts](<https://devfeed.tech/articles/some-fun-software-facts-25505.md>)

Original publisher: [Read original article](<https://buttondown.com/hillelwayne/archive/some-fun-software-facts/>)

Author: Hillel Wayne

Published: 2025-12-10T18:45:37Z

Content type: article

Language: en

Sources: [Newsletter feed for Hillel Wayne's Newsletter](<https://devfeed.tech/sources/newsletter-feed-for-hillel-wayne-s-newsletter.md>)

Topics: [Software](<https://devfeed.tech/topics/software.md>), [Algorithms, Complexity](<https://devfeed.tech/topics/algorithms-complexity.md>), [Simulation](<https://devfeed.tech/topics/simulation.md>), [ASCII](<https://devfeed.tech/topics/ascii.md>), [Vim](<https://devfeed.tech/topics/vim.md>), [Cloudflare](<https://devfeed.tech/topics/cloudflare.md>)

Tags: [algorithms](<https://devfeed.tech/tags/algorithms.md>), [ascii](<https://devfeed.tech/tags/ascii.md>), [cloudflare](<https://devfeed.tech/tags/cloudflare.md>), [simulation](<https://devfeed.tech/tags/simulation.md>), [software](<https://devfeed.tech/tags/software.md>), [vim](<https://devfeed.tech/tags/vim.md>)

### AI overview

A year-end newsletter shares miscellaneous software facts, including a Game of Life implementation of Tetris, leap-second handling, Vim's computational capabilities, ASCII history, impractical faster algorithms, Cloudflare's lava-lamp randomness, and historical sorting-algorithm details.

### Source excerpt

Last newsletter of the year! First some news on Logic for Programmers. Thanks to everyone who donated to the feedchicago charity drive! In total we raised $2250 for Chicago food banks. Proof here. If you missed buying Logic for Programmers real cheap in the charity drive, you can still get it for $10 off with the holiday code hannukah-presents. This will last from now until the end of the year. After that, I'll be raising the price from $25 to $30. Anyway, to make this more than just some record keeping, let's close out with something light. I'm one of those people who loves hearing "fun facts" about stuff. So here's some random fun facts I accumulated about software over the years: In 2017, a team of eight+ programmers successfully implemented Tetris as a game of life simulation. The GoL grid had an area of 30 trillion pixels and implemented a full programmable CPU as part of the project. Computer systems have to deal with leap seconds in order to keep UTC (where one day is 86,400 seconds) in sync with UT1 (where one day is exactly one full earth rotation). The people in charge recently passed a resolution to abolish the leap second by 2035, letting UTC and UT1 slowly drift out of sync. Vim is Turing complete. The backslash character basically didn't exist in writing before 1930, and was only added to ASCII so mathematicians (and ALGOLists) could write /\ and \/. It's popular use in computing stems entirely from being a useless key on the keyboard. Galactic Algorithms are algorithms that are theoretically faster than algorithms we use, but only at scales that make them impractical. For example, matrix multiplication of NxN is normally O(N^2.81). The Coppersmith Winograd algorithm is O(N^2.38), but is so complex that it's vastly slower for even 10,000 x 10,000 matrices. It's still interesting in advancing our mathematical understanding of algorithms! Cloudflare generates random numbers by, in part, taking pictures of 100 lava lamps. Mergesort is older than bubblesor

[Next page](<https://devfeed.tech/sources/newsletter-feed-for-hillel-wayne-s-newsletter.md?cursor=WyIyMDI1LTEyLTEwVDE4OjQ1OjM3KzAwOjAwIiwgImQzNjQ3MTI1LTU0ZDMtNGU1YS04MzBjLWU3Y2Y0YTdiMDQ1YiJd>)