The return of SQL? It's no secret that I'm no fan of ORMs . Most people, on the other hand, find them indispensable. As one reader commented: "I can work with raw SQL ofcourse... but the mapping... oh the mapping..." qfilip This seems to capture something essential. When I discuss ORMs , the most common argument in favour seems to revolve around the amount of boilerplate code required to…
I honestly don't know, but I guess we'll find out. You can't open a social-media feed or listen to a podcast without being bombarded with stories of how LLMs write code faster than programmers. Not only for greenfield development, but also in already-established code bases. As long as you use LLMs as a tool to generate code that humans are ultimately responsible for, the quality of the code still…
JSON is not a good data-interchange format. This article is part of a series called Worse is better , in which I muse on technologies and techniques that became popular despite superior alternatives. Think VHS versus Betamax . In this article, I will argue that XML is superior to JSON in most respects. Lightweight XML # Depending on how old you are, I guess that you have one of two reactions. If…
The most popular technology may not be the best. In 1989 Richard P. Gabriel coined the term Worse is Better , a clearly humorous take on certain tendencies in software development and design as he saw them. I first encountered the term in his book Patterns of Software , and I'm basing the following on that. In essence, another way to state the epigram is that perfection is the enemy of the good.…
Most of my concerns about AI are probably irrelevant, but what if one of them is not? At the intersection of psychology, neuroscience, epistemology, and political science, there's a concept called motivated reasoning . In short, it describes the tendency to arrive at desired conclusions by reasoning processes heavily influenced by individual motivations. An example is a person who finds reasons to…
Objectivity, meritocracy, high bus factor. What's not to like? Most people dislike bureaucracy, and to be honest, so do I. Even so, a subjective distaste is no argument . It may be wise to understand if bureaucracy entails any benefits, so as to not throw out the baby with the bathwater. This essay does not attempt to make the point that more bureaucracy is better. Rather, it argues that some…
A scenario. Here's another speculative scenario about the future of software in an increasingly hostile international environment . It seems indisputable that the future of software development involves substantial use of LLMs . Some people experiment with vibe coding , but I find it more plausible that we'll see widespread use of LLM-based agents that produce code, with continuous, but…
LLM friendliness does not entail competency. One of the many cognitive biases of the human brain is called the halo effect . In short, it describes the tendency to transfer positive impressions of a person or organization from one area to another. If you like a particular actor because of a role, you may think that he or she has good taste in gin, too. When serious fiction authors say something…
Which programming language is best suited for LLM-based generation? I recently asked readers to consider which programming language they would choose for a software system generated by one or more LLMs , and offered these options: Machine code Assembly language Idris I deliberately made the menu unreasonable for a few reasons. I'm well aware that most people who dabble with LLM-based code…
Who is liable for code written by LLMs? It seems as though everyone is talking about agentic AI, and although it's perhaps already subject to semantic diffusion , I understand it as the process of letting one or more LLMs go off and write code on their own. I've toyed enough with it to acknowledge that the potential is undeniable. I can see myself using LLM-based coding agents as team members on a…
A rhetorical poll. Pretend that you don't read or write any of the following programming languages. If you were to let one or more LLMs generate code for a software system, based on your specifications, and possibly interactions (chat) with them, in which of the following languages would you prefer that they generate code? Machine code Assembly language Idris To be clear, while I've phrased this…
Not all software needs to be correct, but a large subset does. Last year, I consulted with an organization that develops and maintains reporting solutions. In a nutshell, they extract data from various line-of-business applications and put them in a reporting-specific data store to create reports for decision makers. For regular readers, this may sound like work bordering on the trivial: Read…
A metaphor. In the mid 2010s I was working with a Danish software development organisation, effectively acting as a lead developer. Because of a shortage of salaried employees, we needed to hire freelancers, and after I had exhausted my local network, I turned to international contacts. One (excellent) addition to the team was Mike Hadlow , who worked out of England. On his first day, we had him…
Two attempts to measure the quality of automated test suites. While test-driven development remains, in my view, the most scientific approach to software testing, I realize that it's still a minority practice. Furthermore, with the rise of AI, it's becoming increasingly common to let LLMs generate tests . Being practical about it, we need to explore how to critique tests; how to measure or…
Get ready for some cryptic infix operators. In a previous article I left you with a remaining problem: A test with an assertion weaker than warranted. In this article, you'll see a few tests like that, and how using lenses may improve the situation. Weak tests # The previous article already showed an example of a test I wasn't fully happy with. For convenience, I'll repeat it here. testCase "Groom…
AI-friendly code? On what's left of software-development social media, I see people complaining that as the size of a software system grows, large language models (LLMs) have an increasingly hard time advancing the system without breaking something else. Some people speculate that the context windows size limit may have something to do with this. As a code base grows, an LLM may be unable to fit…
On epistemological soundness of using LLMs to generate automated tests. For decades, software development thought leaders have tried to convince the industry that test-driven development (TDD) should be the norm. I think so too . Even so, the majority of developers don't use TDD. If they write tests, they add them after having written production code. With the rise of large language models (LLMs,…
Performance and correctness are two independent concerns with overlapping solutions. How do you design, implement, maintain, and test complex filter logic as part of out-of-process (e.g. database) queries? One option is to implement parts of the filtering logic twice: Once as an easily-testable in-memory implementation to ensure correctness, and another, possibly simpler, query using the query…
Using Git for CI is not the same as Tactical Git. Git is such a versatile tool that when discussing it, interlocutors may often talk past each other. One person's use is so different from the way the next person uses it that every discussion is fraught with risk of misunderstandings. This happens to me a lot, because I use Git in two radically different ways, depending on context. Should you…
Don't repeat yourself (DRY) implies O(1) edits. Here's a half-baked idea: We may view coupling in software through the lens of big-O notation . Since this isn't yet a fully-formed idea of mine, this is one of those articles I write in order to learn from the process of having to formulate the idea to other people. Widening the scope of big-O analysis # Big-O analysis is usually described in terms…
We'll get commercial nuclear fusion earlier. Although, as I've described earlier , I tend to be conservative about updating my laptop, I tend to make exceptions for Visual Studio and Visual Studio Code . I was recently perusing the "what's new" notes after updating one or the other, and among all the new AI capabilities that I'm not interested in, I noticed something else: 'improved Git…
Adding Eq instances for better assertions. Most well-written unit tests follow some variation of the Arrange Act Assert pattern. In the Assert phase, you may write a sequence of assertions that verify different aspects of what 'success' means. Even so, it boils down to this: You check that the expected outcome is equal to the actual outcome. Some testing frameworks like to turn the order around,…
You can also make mistakes that compile in Haskell. Seeing a (unit) test fail before making it pass is an important part of empirical software engineering . This is nothing new. We've known about the red-green-refactor cycle for at least twenty years, so we know that ensuring that a test can fail is of the essence. A fundamental problem of automated testing is that we're writing code to test code.…
A Haskell example. Recently, I was doing a Haskell project implementing a cellular automaton according to predefined rules. Specifically, the story was one of Galápagos finches meeting and deciding whether or not to groom each other for parasites, effectively playing out a round of prisoner's dilemma . Each finch is equipped with a particular strategy for repeated play. This strategy is…
You have to read and maintain test code, too. I don't think I've previously published an article with the following simple message, which is clearly an omission on my part. Better late than never, though. Treat test code like production code. You should apply the same coding standards to test code as you do to production code. You should make sure the code is readable, well-factored, goes through…
If you don't see the point, you may be looking in the wrong place. I regularly encounter programmers who are curious about statically typed functional programming, but are struggling to understand the point of sum types (also known as Discriminated Unions, Union Types, or similar). Particularly, I get the impression that recently various thought leaders have begun talking about Result types .…
How do you add tests to a proof-of-concept? Should you? This is the second article in a small series on empirical test-after development . I'll try to answer an occasionally-asked question: Should one use test-driven development (TDD) for prototyping? There are variations on this question, but it tends to come up when discussing TDD. Some software thought leaders proclaim that you should always…
Dispelling a myth I helped propagate. Most people who have been around automated testing for a few years understand that code coverage is a useless target measure . Unfortunately, through a game of Chinese whispers, this message often degenerates to the simpler, but incorrect, notion that code coverage is useless. As I've already covered in that article, code coverage may be useful for other…
Gathering empirical evidence while adding tests to legacy code. This article is part of a short series on empirical test-after techniques . Sometimes, test-driven development (TDD) is impractical. This often happens when faced with legacy code. Although there's a dearth of hard data, I guess that most code in the world falls into this category. Other software thought leaders seem to suggest the…
A few techniques for situations where TDD is impractical. In Epistemology of software I described how test-driven development (TDD) is a scientific approach to software development. By running tests, we conduct falsifiable experiments to gather empirical evidence that corroborate our hypothesis about the software we're developing. TDD is, in my experience, the most effective way to deliver useful…
How do you know that your code works? In 2023 I gave a conference keynote titled Epistemology of software , a recording of which is available on YouTube . In it, I try to answer the question: How do we know that software works? The keynote was for a mixed audience with some technical, but also a big contingent of non-technical, software people, so I took a long detour around general epistemology ,…
Result types are roughly equivalent to exceptions. This article is part of a a series about software design isomorphisms , although naming this one an isomorphism is a stretch. A real isomorphism is when a lossless translation exists between two or more different representations. This article series has already shown a few examples that fit the definition better than what the present article will…
A difficult task may be easier if done sooner. You've probably seen a figure like this before: The point is that as time passes, the cost of doing something increases. This is often used to explain why test-driven development (TDD) or other agile methods are cost-effective alternatives to a waterfall process. Last time I checked, however, there was scant scientific evidence for this curve. Even…
With HUnit and QuickCheck examples. A question had been in the back of my mind for a long time, but I always got caught up in something seemingly more important, so I didn't get around to investigate until recently. It's simply this: How do you compose pure assertions in HUnit or QuickCheck ? Let me explain what I mean, and why this isn't quite as straightforward as it may sound. Assertions as…
Code is a liability, not an asset. It's not a new idea that the more source code you have, the greater the maintenance burden. Dijkstra already touched on this topic in his Turing Award lecture in 1972, and later wrote, "if we wish to count lines of code, we should not regard them as "lines produced" but as "lines spent"" On the cruelty of really teaching computing science , Edsger W. Dijkstra,…
Is TDD white-box testing or black-box testing? Surely you're aware of the terms black-box testing and white-box testing , but have you ever wondered where test-driven development (TDD) fits in that picture? The short answer is that TDD as a software development practice sits somewhere between the two. It really isn't black and white, and exactly where TDD sits on the spectrum changes with…
Are IO expressions really referentially transparent programs? Sometimes, when I discuss functional architecture or the IO container , a reader will argue that Haskell IO really is 'pure', 'referentially transparent', 'functional', or has another similar property. The argument usually goes like this: An IO value is a composable description of an action, but not in itself an action. Since IO is a…
Just because it's possible. Don't do this at work. This is the last article in a series named Alternative ways to design with functional programming . In it, you've seen various suggestions on how to model a non-trivial problem with various kinds of functional-programming patterns. The previous two articles showed how to use free monads to model the problem in Haskell and F# . In this article,…
With an extensive computation expression. This article is part of a series called Alternative ways to design with functional programming . As the title suggests, it examines alternative functional-programming architectures . It does so by looking at the same overall example problem: Calculating song recommendations from a large data set of 'scrobbles'; records of playback data for many users. In…
A surprisingly easy refactoring. This article is part of a larger series titled Alternative ways to design with functional programming . In short, it uses Haskell , F# , and C# to present various internal architectures to deal with an example problem. Please refer to the table of contents included with the first article to get a sense of what has already been covered. In this article, you'll see…
A Golden Hammer. This article is part of a larger article series about alternative ways to design with functional programming , particularly when faced with massive data loads. In previous articles in the series, you've seen various alternatives that may or may not enable you to solve the example problem using functional programming. Each of the previous alternatives, applying the Recawr Sandwich…
Synchronized database reads for testing purposes. In a previous article, you saw how to use a slow Decorator to test for race conditions. Towards the end, I discussed how that solution is only near-deterministic. In this article, I discuss a technique which is, I think, properly deterministic, but unfortunately less elegant. In short, it works by letting a Decorator synchronize reads. The problem…
MailboxProcessors as small Recawr Sandwiches. This article is part of a series named Alternative ways to design with functional programming . As the title implies, over a multitude of articles, I present various alternatives for applying functional programming to a particular problem. When I present the Impureim Sandwich design pattern, the most common reaction is: What if you need to make…
Observables as small Recawr Sandwiches. This article is part of a series titled Alternative ways to design with functional programming . In the previous article in the series , you read some general reflections on a pipes-and-filters architecture. This article gives an example in C#. The code shown here is from the rx branch of the example code Git repository. As the name implies, it uses…
Composing small Recawr Sandwiches. This article is part of a larger series that outlines various alternative ways to design with functional programming . That (first) article contains a table of contents, as well as outlines the overall programme and the running example. In short, the example is a song recommendation engine that works on large data sets. Previous articles in this series have…
A single Haskell script file. I recently took the final exam in a course on programming language design. One of the questions was about a tiny language, and since this was a take-home exam running over many days, I had time to spare. Although it wasn't part of any questions, I decided to write an interpreter to back up some claims I made in my answers. This article documents my prototype parser…
Traversing lists of IO. A refactoring. This article is part of a series named Alternative ways to design with functional programming . In the previous article , you saw how to refactor the example code base to a composition of standard F# combinators. It's a pragmatic solution to the problem of dealing with lots of data in a piecemeal fashion, but although it uses concepts and programming…
Traversing sequences of tasks. A refactoring. This article is part of a series named Alternative ways to design with functional programming . In the previous article , you saw how to refactor the example code base to a composition of standard combinators. It's a pragmatic solution to the problem of dealing with lots of data in a piecemeal fashion, but although it uses concepts and programming…
LINQ-style composition, including SelectMany and Traverse. This article is part of a larger series titled Alternative ways to design with functional programming . In the previous article , I described, in general terms, a pragmatic small-scale architecture that may look functional, although it really isn't. Please consult the previous articles for context about the example code base. The code…
Interleaving impure actions with pure functions. Not really functional programming. This article is part of a larger article series about alternative ways to design with functional programming , particularly when faced with massive data loads. In the previous few articles, you saw functional architecture at its apparent limit. With sufficiently large data sizes, the Impureim Sandwich pattern…