Things I do when I’m writing code that don’t look like writing code

Non-exhaustive list of things I’m doing when I’m writing code, that don’t look like “writing code”:

  • thinking
  • researching
  • contextualising
  • testing
  • measuring
  • documenting
  • communicating
  • planning
  • future-proofing
  • educating
  • learning
  • expressing
  • anticipating
  • discovering
  • inventing
  • experimenting
  • debugging
  • analysing
  • monitoring

For all its faults, an AI agent might “write code” faster than me.

But that’s only a part of the process.

My typing speed is not the bottleneck.

Subverting AI Agent Logging with a Git Post-Commit Hook

Last night I was chatting to my friend (and fellow Three Rings volunteer) Ollie about our respective workplaces and their approach to AI-supported software engineering, and it echoed conversations I’ve had with other friends. Some workplaces, it seems, are leaning so-hard into AI-supported software development that they’re berating developers who seem to be using the tools less than their colleagues!

That’s a problem for a few reasons, principal among them that AI does not make you significantly faster but does make you learn less.1. I stand by the statement that AI isn’t useless, and I’ve experimented with it for years. But I certainly wouldn’t feel very comfortable working somewhere that told me I was underperforming if, say, my code contributions were less-likely than the average to be identifiably “written by an AI”.

Even if you’re one of those folks who swears by your AI assistant, you’ve got to admit that they’re not always the best choice.

Copilot review of some code on GitHub, in which it's telling me that I should have included an .agent-logs/... file in which my AI agent describes how it helped, but I'm responding to say that 'shockingly' I wrote it without the help of AI, and telling Copilot to shut up.
I ran into something a little like what Ollie described when an AI code reviewer told me off for not describing how my AI agent assisted me with the code change… when no AI had been involved: I’d written the code myself.2

I spoke to another friend, E, whose employers are going in a similar direction. E joked that at current rates they’d have to start tagging their (human-made!) commits with fake AI agent logs in order to persuade management that their level of engagement with AI was correct and appropriate.3

Supposing somebody like Ollie or E or anybody else I spoke to did feel the need to “fake” AI agent logs in order to prove that they were using AI “the right way”… that sounds like an excuse for some automation!

I got to thinking: how hard could it be to add a git hook that added an AI agent’s “logging” to each commit, as if the work had been done by a robot?4

Turns out: pretty easy…

Animation showing a terminal. The developer switches to a branch, adds two modifications, and commits them. Afterwards, the log and filesystem show that a log file has been created crediting (fictional) AI bot 'frantic' with the change.
To try out my idea, I made two changes to a branch. When I committed, imaginary AI agent ‘frantic’ took credit, writing its own change log. Also: asciinema + svg-term remains awesome.

Here’s how it works (with source code!). After you make a commit, the post-commit hook creates a file in .agent-logs/, named for your current branch. Each commit results in a line being appended to that file to say something like [agent] first line of your commit message, where agent is  the name of the AI agent you’re pretending that you used (you can even configure it with an array of agent names and it’ll pick one at random each time: my sample code uses the names agent, stardust, and frantic).

There’s one quirk in my code. Git hooks only get the commit message (the first line of which I use as the imaginary agent’s description of what it did) after the commit has taken place. Were a robot really used to write the code, it’d have updated the file already by this point. So my hook has to do an --amend commit, to retroactively fix what was already committed. And to do that without triggering itself and getting into an infinite loop, it needs to use a temporary environment variable. Ignoring that, though, there’s nothing particularly special about this code. It’s certainly more-lightweight, faster-running, and more-accurate than a typical coding LLM.

Sure, my hook doesn’t attempt to write any of the code for you; it just makes it look like an AI did. But in this instance: that’s a feature, not a bug!

Footnotes

1 That research comes from Anthropic. Y’know, the company who makes Claude, one of the most-popular AIs used by programmers.

2 Do I write that much like an AI? Relevant XKCD.

3 Using “proportion of PRs that used AI” as a metric for success seems to me to be just slightly worse than using “number of lines of code produced”. And, as this blog post demonstrates, the former can be “gamed” just as effectively as the latter (infamously) could.

4 Obviously – and I can’t believe I have to say this – lying to your employer isn’t a sensible long-term strategy, and instead educating them on what AI is (if anything) and isn’t good for in your workflow is a better solution in the end. If you read this blog post and actually think for a moment hey, I should use this technique, then perhaps there’s a bigger problem you ought to be addressing!

× ×

To really foul things up you need an AI

Today, an AI review tool used by my workplace reviewed some code that I wrote, and incorrectly claimed that it would introduce a bug because a global variable I created could “be available to multiple browser tabs” (that’s not how browser JavaScript works).

Just in case I was mistaken, I explained to the AI why I thought it was wrong, and asked it to explain itself.

To do so, the LLM wrote a PR to propose adding some code to use our application’s save mechanism to pass the data back, via the server, and to any other browser tab, thereby creating the problem that it claimed existed.

This isn’t even the most-efficient way to create this problem. localStorage would have been better.

So in other words, today I watched an AI:
(a) claim to have discovered a problem (that doesn’t exist),
(b) when challenged, attempt to create the problem (that wasn’t needed), and
(c) do so in a way that was suboptimal.

Humans aren’t perfect. A human could easily make one of these mistakes. Under some circumstances, a human might even have made two of these mistakes. But to make all three? That took an AI.

What’s the old saying? “To err is human, but to really foul things up you need a computer.”

Mocking SharePoint

Highlight of my workday was debugging an issue that turned out to be nothing like what the reporter had diagnosed.

The report suggested that our system was having problems parsing URLs with colons in the pathname, suggesting perhaps an encoding issue. It wasn’t until I took a deep dive into the logs that I realised that this was a secondary characteristic of many URLs found in customers’ SharePoint installations. And many of those URLs get redirected. And SharePoint often uses relative URLs when it sends redirections. And it turned out that our systems’ redirect handler… wasn’t correctly handling relative URLs.

It all turned into a hundred line automated test to mock SharePoint and demonstrate the problem… followed by a tiny two-line fix to the actual code. And probably the most-satisfying part of my workday!

Why does SSH send 100 packets per keystroke?

This is a repost promoting content originally published elsewhere. See more things Dan's reposted.

Further analysis on a smaller pcap pointed to these mysterious packets arriving ~20ms apart.

This was baffling to me (and to Claude Code). We kicked around several ideas like:

  • SSH flow control messages
  • PTY size polling or other status checks
  • Some quirk of bubbletea or wish

One thing stood out – these exchanges were initiated by my ssh client (stock ssh installed on MacOS) – not by my server.

In 2023, ssh added keystroke timing obfuscation. The idea is that the speed at which you type different letters betrays some information about which letters you’re typing. So ssh sends lots of “chaff” packets along with your keystrokes to make it hard for an attacker to determine when you’re actually entering keys.

That makes a lot of sense for regular ssh sessions, where privacy is critical. But it’s a lot of overhead for an open-to-the-whole-internet game where latency is critical.

Keystroke timing obfuscation: I could’ve told you that! Although I wouldn’t necessarily have leapt to the possibility of mitigating it server-side by patching-out support for (or at least: the telegraphing of support for!) it; that’s pretty clever.

Altogether this is a wonderful piece demonstrating the whole “engineer mindset”. Detecting a problem, identifying it, understanding it, fixing it, all tied-up in an engaging narrative.

And after playing with his earlier work, ssh tiny.christmas – which itself inspired me to learn a little Bubble Tea/Wish (I’ve got Some Ideas™️) – I’m quite excited to see where this new ssh-based project of Royalty’s is headed!

Nostalgia, Music, and Computers

Duration

Podcast Version

This post is also available as a podcast. Listen here, download for later, or subscribe wherever you consume podcasts.

This is a blog post about things that make me nostalgic for other things that, objectively, aren’t very similar…

When I hear Dawnbreaker, I feel like I’m nine years old…

…and I’ve been allowed to play OutRun on the arcade cabinet at West View Leisure Centre. My swimming lesson has finished, and normally I should go directly home.

On those rare occasions I could get away1 with a quick pause in the lobby for a game, I’d gravitate towards the Wonderboy machine. But there was something about the tactile controls of OutRun‘s steering wheel and pedals that gave it a physicality that the “joystick and two buttons” systems couldn’t replicate.

The other thing about OutRun was that it always felt… fast. Like, eye-wateringly fast. This was part of what gave it such appeal2.

OutRun‘s main theme, Magical Sound Shower, doesn’t actually sound much like Dawnbreaker. But both tracks somehow feel like… “driving music”?

(It should, I suppose: Metrik wrote Dawnbreaker explicitly for that purpose in the first place, for use in a videogame I haven’t played3.)

But somehow when I’m driving or cycling and it this song comes on, I’m instantly transported back to those occasionally-permitted childhood games of OutRun4.

When I start a new Ruby project, I feel like I’m eleven years old…

…and I’m writing Locomotive BASIC on the family’s Amstrad CPC. Like many self-taught coders in the 1980s, my journey as a programmer begin with BASIC. When I transitioned from that to more “grown-up” languages5 I missed the feeling of programming in an environment where every line brought me joy.

Animation of an Amstrad CPC 6128 on which a program is typed and then executed. The program clears the screen and then prints the message 'Thanks for visiting DANQ.ME".
It’s not quite a HELLO WORLD, but it’s pretty-similar.

At first I assumed that the tedious bits and the administrative overhead (linking, compiling, syntactical surprises, arcane naming conventions…) was just what “real”, “grown-up” programming was supposed to feel like. But Ruby helped remind me that programming can be fun for its own sake. Not just because of the problems you’re solving or the product you’re creating, but just for the love of programming.

The experience of starting a new Ruby project feels just like booting up my Amstrad CPC and being able to joyfully write code that will just work.

I still learn new programming languages because, well, I love doing so. But I’m yet to find one that makes me want to write poetry in it in the way that Ruby does.

When I hear In Yer Face, I feel like I’m thirteen years old…

…and I’m painting Advanced HeroQuest miniatures6 in the attic at my dad’s house.

I’ve cobbled together a stereo system of my very own, mostly from other people’s castoffs, and set it up in “The Den”, our recently-converted attic7, and my friends and I would make and trade mixtapes with one another. One tape began with 808 State’s In Yer Face8, and it was often the tape that I would put on when I’d sit down to paint.

Several jigsaw-edged board game pieces lay out a dungeon map, with painted plastic minatures representing doors and characters. A party of four adventurers have just opened a door into a chamber containing five skaven (ratmen), guarding a treasure chest.
Advanced HeroQuest came with some fabulously ornate secondary components, like the doors that were hinged so their their open/closed state could be toggled, and I spent way too long painting almost the entirety of my base set.

In a world before CD audio took off, “shuffle” wasn’t a thing, and we’d often listen to all of the tracks on a medium in sequence9.

That was doubly true for tapes, where rewinding and fast-forwarding took time and seeking for a particular track was challenging compared to e.g. vinyl. Any given song would loop around a lot if I couldn’t be bothered to change tapes, instead just flipping again and again10. But somehow it’s whenever I hear In Yer Face11 that I’m transported right back to that time, in a reverie so corporeal that I can almost smell the paint thinner.

When I see a personal Web page, I (still) feel like I’m fifteen years old…

…and the Web is on the cusp of becoming the hot “killer application” for the Internet. I’ve been lucky enough to be “online” for a few years by now12, and basic ISP-provided hosting would very soon be competing with cheap, free, and ad-supported services like Geocities to be “the place” to keep your homepage.

Since its early days, the Web has always been an expressive medium. Open a Web browser, and you’re seeing a blank canvas of potential. And with modern browser debug tools, you don’t even have to reach for your text editor to begin to create in that medium.

Fresh web browser, semitransparent, on an artistic 'airy' background, with a caption to say 'The entire potential of the Web, and by proxy, the World, exists within this newly-opened window.
I don’t often see a browser with no tabs open13. But a fresh tab still gives me a tingle when I remember that it might take me anywhere!

The limitations of that medium in the pre-CSS era were a cause for inspiration, not confinement: web pages of the mid-1990s would use all kinds of imaginative tricks to lay out and style their content!

Nowadays, even with a hugely-expanded toolbox, virtually every corporate homepage fundamentally looks the same:

  • Logo in the top left
  • Search and login in the top right, if applicable
  • A cookie/privacy notice covering everything until you work out the right incantation to make it go away without surrendering your firstborn child
  • A “hero banner
  • Some “below the fold” content that most people skip over
  • A fat footer with several columns of links, to ensure that all the keywords are there so that people never have to see this page and the search engine will drop them off at relevant child page and not one of their competitors
  • Finally, a line of icons representing various centralised social networks: at least one is out-of-date, either because (a) it’s been renamed, (b) it’s changed its branding, or (c) nobody with any moral fortitude uses that network any more14

But before the corporate Web became the default, personal home pages brought a level of personality that for a while I worried was forever dead.

But… personal home pages didn’t die: everybody is free to write websites, so they’re still out there15, and they’re amazing. Look at this magic:

A handful of the personal home pages I visited while writing this article16. Don’t they just make you want to give the Web an enormous hug?

Last year, I wrote:

Writing HTML is punk rock. A “platform” is the tool of the establishment.

That still feels right to me. 🤘


So… it turns out that I get nostalgic about technology in the same way as I get nostalgic about music.

Footnotes

1 My dad in particular considered arcade games financially wasteful when we, y’know, had a microcomputer at home that could load a text-based adventure from an audiotape and be ready to play in “only” about 3-5 minutes.

2 Have you played Sonic Racing: CrossWorlds? The first time I played it I was overwhelmed by the speed and colours of the game: it’s such a high-octane visual feast. Well that’s what OutRun felt like to those of us who, in the 1980s, were used to much-simpler and slower arcade games.

3 Also, how cool is it that Metrik has a blog, in this day and age? Max props.

4 Did you hear, by the way, that there’s talk of a movie adaptation of OutRun, which could turn out to be the worst videogame-to-movie concept that I’ll ever definitely-watch.

5 In very-approximate order: C, Assembly, Pascal, HTML, Perl, Visual Basic (does that even count as a “grown-up” language?), Java, Delphi, JavaScript, PHP, SQL, ASP (classic, pre-.NET), CSS, Lisp, C#, Ruby, Python (though I didn’t get on with it so well), Go, Elixir… plus many others I’m sure!

6 Or possibly they were Warhammer Quest miniatures by this point; probably this memory spans one, and also the other, blended together.

7 Eventually my dad and I gave up on using the partially-boarded loft to intermittently build a model railway layout, mostly using second-hand/trade-in parts from “Trains & Transport”, which was exactly the nerdy kind of model shop you’re imagining right now: underlit and occupied by a parade of shuffling neckbeards, between whom young-me would squeeze to see if the mix-and-match bin had any good condition HO-gauge flexitrack. We converted the attic and it became “The Den”, a secondary space principally for my use. This was, in the most part, a concession for my vacating of a large bedroom and instead switching to the smallest-imaginable bedroom in the house (barely big enough to hold a single bed!), which in turn enabled my baby sister to have a bedroom of her own.

8 My copy of In Yer Face was possibly recorded from the radio by my friend ScGary, who always had a tape deck set up with his finger primed close to the record key when the singles chart came on.

9 I soon learned to recognise “my” copy of tracks by their particular cut-in and -out points, static and noise – some of which, amazingly, survived into the MP3 era – and of course the tracks that came before or after them, and there are still pieces of music where, when I hear them, I “expect” them to be followed by something that they used to some mixtape I listened to a lot 30+ years ago!

10 How amazing a user interface affordance was it that playing one side of an audio cassette was mechanically-equivalent to (slowly) rewinding the other side? Contrast other tape formats, like VHS, which were one-sided and so while rewinding there was literally nothing else your player could be doing. A “full” audio cassette was a marvellous thing, and I especially loved the serendipity where a recognisable “gap” on one side of the tape might approximately line-up with one on the other side, meaning that you could, say, flip the tape after the opening intro to one song and know that you’d be pretty-much at the start of a different one, on the other side. Does any other medium have anything quite analogous to that?

11 Which is pretty rare, unless I choose to put it on… although I did overhear it “organically” last summer: it was coming out of a Bluetooth speaker in a narrowboat moored in the Oxford Canal near Cropredy, where I was using the towpath to return from a long walk to nearby Northamptonshire where I’d been searching for a geocache. This was a particularly surprising place to overhear such a song, given that many of the boats moored here probably belonged to attendees of Fairport’s Cropredy Convention, at which – being a folk music festival – one might not expect to see significant overlap of musical taste with “Madchester”-era acid house music!

12 My first online experiences were on BBS systems, of which my very first was on a mid-80s PC1512 using a 2800-baud acoustic coupler! I got onto the Internet at a point in the early 90s at which the Web existed… but hadn’t yet demonstrated that it would eventually come to usurp the services that existed before it: so I got to use Usenet, Gopher, Telnet and IRC before I saw my first Web browser (it was Cello, but I switched to Netscape Navigator soon after it was released).

13 On the rare occasion I close my browser, these days, it re-opens with whatever hundred or so tabs I was last using right back where I left them. Gosh, I’m a slob for tabs.

14 Or, if it’s a Twitter icon: all three of these.

15 Of course, they’re harder to find. SEO-manipulating behemoths dominate the search results while social networks push their “apps” and walled gardens to try to keep us off the bigger, wider Web… and the more you cut both our of your online life, the calmer and happier you’ll be.

16 The sites featured in the video are: praze, elle’s homepage, sctech, Konfetti Explorations (Marisabel), Frills (check her character sheet “about” page!), mrkod, Raven Winters, Cobb, ajazz, Yusuf Ertan, Alvin Bryan, Armando Cordova, Ens/DepartedGlories, and Jamie Tanna.

× × ×

DOCTYPE

This weekend, I received my copy of DOCTYPE, and man: it feels like a step back to yesteryear to type in a computer program from a magazine: I can’t have done that in at least thirty years.

Dan sits at a cluttered desk reading a copy of DOCTYPE, a magazine with an aggressively 'cyberspace circa 1990' graphic design cover.
I mentioned that I’ve been on a bit of a nostalgic Web Revivalist kick lately, right?

So yeah, DOCTYPE is a dead-tree (only) medium magazine containing the source code to 10 Web pages which, when typed-in to your computer, each provide you with some kind of fun and interactive plaything. Each of the programs is contributed by a different author, including several I follow and one or two whom I’m corresponded with at some point or another, and each brings their own personality and imagination to their contribution.

I opted to start with Stuart Langridge‘s The Nine Pyramids, a puzzle game about trying to connect all nodes in a 3×3 grid in a continuous line bridging adjacent (orthogonal or diagonal) nodes without visiting the same node twice nor moving in the same direction twice in a row (that last provision is described as “not visiting three in a straight line”, but I think my interpretation would have resulted in simpler code: I might demonstrate this, down the line!).

Open magazine showing program code in front of a screen showing a text editor and the running program.
The puzzle actually made me stop to think about it for a bit, which was unexpected and pleasing!

Per tradition with this kind of programming, I made a couple of typos, the worst of which was missing an entire parameter in a CSS conic-gradient() which resulted in the majority of the user interface being invisible: whoops! I found myself reminded of typing-in the code for Werewolves and Wanderer from The Amazing Amstrad Omnibus, whose data section – the part most-liable to be affected by a typographic bug without introducing a syntax error – had a helpful “checksum” to identify if a problem had occurred, and wishing that such a thing had been possible here!

But thankfully a tiny bit of poking in my browser’s inspector revealed the troublesome CSS and I was able to complete the code, and then the puzzle.

I’ve really been enjoying DOCTYPE, and you can still buy a copy if you’d like one of your own. It manages to simultaneously feel both fresh and nostalgic, and that’s really cool.

Are you closer to Ireland or Scotland?

I nerdsniped myself today when, during a discussion on the potential location of a taekwondo tournament organised by our local martial arts school, somebody claimed that Scotland would be “nearer” than Ireland.

WhatsApp conversation. Somebody says "Scotland preferred just because nearer/cheaper!", to which Dan replies, with a map showing rulers connecting Witney to the nearest relevant borders, "Certainly cheaper, but (/puts pedantry hat on/) Witney's actually about 40km nearer to Ireland than Scotland. (/takes pedantry hat off and apologises/) (Sorry, sorry, sorry... geography nerd... sorry.)"
I don’t dispute that somebody living near me can get to Scotland faster than Ireland, unless they can drive at motorway speeds across Wales… and the Irish Sea. But the word they used was nearer, and I can be a pedantic arse.

But the question got me thinking:

Could I plot a line across Great Britain, showing which parts are closer to Scotland and which parts are closer to Ireland?

If the England-facing Irish and Scottish borders were completely straight, one could simply extend the borders until they meet, bisect the angle, and we’d be done.

Extremely simplified hand-drawn map of the British Isles, with the (artificially-straight) borders of Scotland and Ireland extended to meet and then their angle bisected to produce a line that splits England by proximity to each country.
Of course, the borders aren’t straight. They also don’t look much like this. I should not draw maps.

In reality, the border between England and Scotland is a winding mess, shaped by 700 years of wars and treaties1. Treating the borders as straight lines is hopelessly naive.

What I’m really looking for… is a Voronoi partition

Animation showing colours gradually expanding from points on a plane, carving out borders along their edges.
Voronoi diagrams are pretty, and cool, and occasionally even useful! This one expands from points, but there’s no reason you can’t expand from a line (line a border!) instead.

My Python skills are pretty shit, but it’s the best tool for the job for geohacking2. And so, through a combination of hacking, tweaking, and crying, I was able to throw together a script that produces a wonderful slightly-wiggly line up the country.

A line cuts from North-West to South-East through England and Wales.
The entire island of Ireland is used here to determine boundaries (you can tell because otherwise parts of County Antrim, in Northern Ireland, would be marked as closer to Scotland than the Republic of Ireland: which they are, of course, but the question was about England!).

Once you’ve bisected England in this way – into parts that are “closer to Ireland” versus parts that are “closer to Scotland”, you start to spot all kinds of interesting things3.

Like: did you know that the entire subterranean part of the Channel Tunnel is closer to Scotland than it is to Ireland… except for the ~2km closest to the UK exit.

Map showing the Le Shuttle terminal and the entrance to the Channel Tunnel, marked up to show how the first 2km of the underground part (only) are closer to Ireland than to Scotland.

A little further North: London’s six international airports are split evenly across the line, with Luton, Stansted and Southend closer to Scotland… and City, Heathrow and Gatwick closer to Ireland.

Map showing the locations of London's six major airports: three North-East of the line, three South-West of it.

The line then pretty-much bisects Milton Keynes, leaving half its population closer to Scotland and half closer to Ireland, before doing the same to Daventry, before – near Sutton Coldfield – it drives right through the middle of the ninth hole of the golf course at the Lea Marston Hotel.

Players tee off closer to Ireland and – unless they really slice it – their ball lands closer to Scotland:

Aerial photograph of a golf course with a red line superimposed across it. Near the green, a speech bubble has a golfer saying "Next shot's going to Scotland!"

In Cannock, it bisects the cemetery, dividing the graves into those on the Scottish half and those in the Irish half:

Aerial photography of Cannock Cemetery, bisected with a thick red line.

The line crosses the Welsh border at the River Dee, East of Wrexham, leaving a narrow sliver of Wales that’s technically closer to Scotland than it is to Ireland, running up the coastline from Connah’s Quay to Prestatyn and going as far inland as Mold before – as is the case in most of Wales – you’re once again closer to Ireland:

Highlighted fragment of Wales along the West bank of the River Dee and stretching about 10-12km inland.
If you live in Flint or Mold, ask your local friends whether they live closer to Ireland or Scotland. The answer’s Scotland, and I’m confident that’ll surprise them.

I’d never have guessed that there were any parts of Wales that were closer to Scotland than they were to Ireland, but the map doesn’t lie4

Anyway: that’s how I got distracted, today. And along the way I learned a lot about geodata encoding, a little about Python, and a couple of surprising things about geography5.

Footnotes

1 Not to mention the crazy history of places like Berwick-upon-Tweed, which has jumped the border several times, and Ba Green, ownership of which has traditionally been decided by game of football.

2 Or, at least: it’s the one that’s most-widely used and so I could find lots of helpful StackOverflow answers when I got stuck!

3 Interesting… if you’re specifically looking for some geographical trivia, that is!

4 Okay, the map lies a little. My program was only simple so it plotted everything on a flat plane, failing to accommodate for Earth’s curvature. The difference is probably marginal, but if you happen to live on or very close to the red line, you might need to do your own research!

5 Like: Chester and Rugby are closer to Scotland than they are to Ireland, and Harpenden and Towcester are closer to Ireland than they are to Scotland! Who knew?

× × × × × × × × ×

The Scroll Art Museum

This is a repost promoting content originally published elsewhere. See more things Dan's reposted.

Scroll art is a form of ASCII art where a program generates text output in a command line terminal. After the terminal window fills, it begins to scroll the text upwards and create an animated effect. These programs are simple, beautiful, and accessible as programming projects for beginners. The SAM is a online collection of several scroll art examples.

Here are some select pieces:

  • Zig-zag, a simple periodic pattern in a dozen lines of code.
  • Orbital Travels, sine waves intertwining.
  • Toggler, a woven triangular pattern restricted to two characters.
  • Proton Stream, a rapid, chaotic lightning pattern.

There are two limitations to most scroll art:

  • Program output is limited to text (though this could include emoji and color.)
  • Once printed, text cannot be erased. It can only scroll up.

But these restrictions compel creativity. The benefit of scroll art is that beginner programmers can create scroll art apps with a minimal amount of experience. Scroll art requires knowing only the programming concepts of print, looping, and random numbers. Every programming langauge has these features, so scroll art can be created in any programming language without additional steps. You don’t have to learn heavy abstract coding concepts or configure elaborate software libraries.

Okay, so: scroll art is ASCII art, except the magic comes from the fact that it’s very long and as your screen scrolls to show it, an animation effect becomes apparent. Does that make sense?

Here, let me hack up a basic example in… well, QBASIC, why not:

Anyway, The Scroll Art Museum has lots of them, and they’re much better than mine. I especially love the faux-parallax effect in Skulls and Hearts, created by a “background” repeating pattern being scrolled by a number of lines slightly off from its repeat frequency while a foreground pattern with a different repeat frequency flies by. Give it a look!

Lowriders & websites

This is a repost promoting content originally published elsewhere. See more things Dan's reposted.

I think of ElonStan420 standing in that exhibit hall, eyeing those cars with disdain because all that time, energy, care, and expression “doesn’t really matter”. Those hand-painted pinstripes don’t make the car faster or cheaper. Chrome-plated everything doesn’t make it more efficient. No one is going to look under the hood anyway.

Don’t read the comments on HackerNews, Adam! (I say this, but I’ve yet to learn not to do so myself, when occasionally my writing escapes from my site and finds its way over there.)

But anyway, this is a fantastic piece about functionalism. Does it matter whether your website has redundant classes defined in the HTML? It renders the same anyway, and odds are good that nobody will ever notice! I’m with Adam: yes, of course it can matter. It doesn’t have to, but coding is both a science and an art, and art matters.

Should every website be the subject of maximal craft? No, of course not. But in a industry rife with KPI-obsessed, cookie-cutter, vibe-coded, careless slop, we could use more lowriders.

Well said, Adam.

Four perspectives on AI

This is a repost promoting content originally published elsewhere. See more things Dan's reposted.

A two-axis graph labeled 'Beliefs about AI' with the x-axis of 'Transformative' and the y-axis of 'Positive'. The field is divided into four quadrants: 'Skeptical' in the bottom-left, 'Wary' in the bottom-right, 'Pragmatist' in the top-left, and 'Optimist' in the top-right.

I’ve grouped these four perspectives, but everything here is a spectrum. Depending on the context or day, you might find yourself at any point on the graph. And I’ve attempted to describe each perspectively [sic] generously, because I don’t believe that any are inherently good or bad. I find myself switching between perspectives throughout the day as I implement features, use tools, and read articles. A good team is probably made of members from all perspectives.

Which perspective resonates with you today? Do you also find yourself moving around the graph?

An interesting question from Sean McPherson. He sounds like he’s focussed on LLMs for software development, for which I’ve drifted around a little within the left-hand-side of the graph. But perhaps right now, this morning, you could simplify my feelings like this:

The same graph with a hand-drawn arrow moving from the Skeptical to the Pragmatic quadrants.My stance is that AI-assisted coding can be helpful (though the question remains open about whether it’s “worth it”), so long as you’re not trying to do anything that you couldn’t do yourself, and you know how you’d go about doing it yourself. That is: it’s only useful to accelerate tasks that are in your “known knowns” space.

As I’ve mentioned: the other week I had a coding AI help me with some code that interacted with the Google Sheets API. I know exactly how I’d go about it, but that journey would have to start with re-learning the Google Sheets API, getting an API key and giving it the appropriate permissions, and so on. That’s the kind of task that I’d be happy to outsource to a less-experienced programmer who I knew would bring a somewhat critical eye for browsing StackOverflow, and then give them some pointers on what came back, so it’s a fine candidate for an AI to step in and give it a go. Plus: I’d be treating the output as “legacy code” from the get-go, and (because the resulting tool was only for my personal use) I wasn’t too concerned with the kinds of security and accessibility considerations that GenAI can often make a pig’s ear of. So I was able to palm off the task onto Claude Sonnet and get on with something else in the meantime.

If I wanted to do something completely outside of my wheelhouse: say – “write a program in Fortran to control a robot arm” – an AI wouldn’t be a great choice. Sure, I could “vibe code” something like that, but I’d have no idea whether what it produced was any good! It wouldn’t even be useful as a springboard to learning how to do that, because I don’t have the underlying fundamentals in robotics nor Fortran. I’d be producing AI slop in software form: the kind of thing that comes out when non-programmers assume that AI can completely bridge the gap between their great business idea and a fully working app!

The latest episode of South Park kinda nailed parodying the unrealistic expectations that some folks seem to put on generative AI: treating it as intelligent or as a friend is unhealthy and dangerous!

They’ll get a prototype that seems to do what you want, if you squint just right, but the hard part of software engineering isn’t making a barebones proof-of-concept! That’s the easy bit! (That’s why AI can do it pretty well!) The hard bit is making it work all the time, every time; making it scale; making it safe to use; making it maintainable; making it production-ready… etc.

But I do benefit from coding AI sometimes. GenAI’s good at summarisation, which in turn can make it good at relatively-quickly finding things in a sprawling codebase where your explanation of those things is too-woolly to use a conventional regular expression search. It’s good at generating boilerplate that’s broadly-like examples its seen before, which means it can usually be trusted to put together skeleton applications. It’s good at “guessing what comes next” – being, as it is, “fancy autocomplete” – which means it can be helpful for prompting you for the right parameters for that rarely-used function or for speculating what you might be about to do with the well-named variable you just created.

Anyway: Sean’s article was pretty good, and it’s a quick and easy read. Once you’ve read it, perhaps you’ll share where you think you sit, on his diagram?

× ×

Just a Little More Context Bro, I Promise, and It’ll Fix Everything

This is a repost promoting content originally published elsewhere. See more things Dan's reposted.

Solving problems with LLMs is like solving front-end problems with NPM: the “solution” comes through installing more and more things — adding more and more context, i.e. more and more packages.

  • LLM: Problem? Add more context.
  • NPM: Problem? There’s a package for that.

As I’m typing this, I’m thinking of that image of the evolution of the Raptor engine, where it evolved in simplicity:

Photograph of three versions of the raptor engine, each one getting progressively simplified in mechanical parts.

This stands in contrast to my working with LLMs, which often wants more and more context from me to get to a generative solution:

Photograph of three versions of the raptor engine, but the image is reversed showing the engine get progressively complicated in mechanical parts over time. Each engine represents an LLM prompt.

Jim Nielsen speaks to my experience, here. Because a programming LLM is simply taking inputs (all of your code, plus your prompt), transforming it through statistical analysis, and then producing an output (replacement code), it struggles with refactoring for simplicity unless very-carefully controlled. “Vibe coding” is very much an exercise in adding hacks upon hacks… like the increasingly-ludicrous epicycles introduced by proponents of geocentrism in its final centuries before the heliocentric model became fully accepted.

Geocentric representation of the apparent motion of the Sun, Mercury, and Venus from the Earth, based on 15th century diagrams. It consists of many looping spirals approaching and then withdrawing from the Earth as they orbit around it.
This mess used to be how many perfectly smart people imagined the movements of the planets. When observations proved it couldn’t be right, they’d just add more complexity to catch the edge cases.

I don’t think that AIs are useless as a coding tool, and I’ve successfully used them to good effect on several occasions. I’ve even tried “vibe coding”, about which I fully agree with Steve Krouse‘s observation that “vibe code is legacy code”. Being able to knock out something temporary, throwaway, experimental, or for personal use only… while I work on something else… is pretty liberating.

For example: I couldn’t remember my Google Sheets API and didn’t want to re-learn it from the sprawling documentation site, but wanted a quick personal tool to manipulate such a sheet from a remote system. I was able to have an AI knock up what I needed while I cooked dinner for the kids, paying only enough attention to check-in on its work. Is it accessible? Is it secure? Is it performant? Is it maintainable? I can’t answer any of those questions, and so as a professional software engineer I have to reasonably assume the answer to all of them is “no”. But its only user is me, it does what I needed it to do, and I didn’t have to shift my focus from supervising children and a pan in order to throw it together!

Anyway: Jim hits the nail on the head here, as he so often does.

× × ×

Note #26999

In my first few weeks at my new employer, my code contributions have added 218 lines of code, deleted 2,663. Only one of my PRs has resulted in a net increase in the size of their codebases (by two lines).

GitHub change summary showing +218 lines added, -2,663 lines removed.

I need to pick up the pace if I’m going to reach the ultimate goal of deleting ALL of the code within my lifetime. (That’s the ultimate aim, right?)

×

ArtificialCast

This is a repost promoting content originally published elsewhere. See more things Dan's reposted.

Type-safe transformation powered by inference.

ArtificialCast is a lightweight, type-safe casting and transformation utility powered by large language models. It allows seamless conversion between strongly typed objects using only type metadata, JSON schema inference, and prompt-driven reasoning.

Imagine a world where Convert.ChangeType() could transform entire object graphs, infer missing values, and adapt between unrelated types – without manual mapping or boilerplate.

ArtificialCast makes that possible.

Features

  • Zero config – Just define your types.
  • Bidirectional casting – Cast any type to any other.
  • Schema-aware inference – Auto-generates JSON Schema for the target type.
  • LLM-powered transformation – Uses AI to “fill in the blanks” between input and output.
  • Testable & deterministic-ish – Works beautifully until it doesn’t.

As beautiful as it is disgusting, this C# is fully-functional and works exactly as described… and yet you really, really should never use it (which its author will tell you, too).

Casting is the process of transforming a variable of one type into one of another. So for example you might cast the number 3 into a string and get "3" (though of course this isn’t the only possible result: "00000011" might also be a valid representation, depending on the circumstances1).

Casting between complex types defined by developers is harder and requires some work. Suppose you have a User model with attributes like “username”, “full name”, “hashed password”, “email address” etc., and you want to convert your users into instances of a new model called Customer. Some of the attributes will be the same, some will be absent, and some will be… different (e.g. perhaps a Customer has a “first name” and “last name” instead of a “full name”, and it’s probably implemented wrong to boot).

The correct approach is to implement a way to cast one as the other.

The very-definitely incorrect approach is to have an LLM convert the data for you. And that’s what this library provides.

ArtificialCast is a demonstration of what happens when overhyped AI ideas are implemented exactly as proposed – with no shortcuts, no mocking, and no jokes.

It is fully functional. It passes tests. It integrates into modern .NET workflows. And it is fundamentally unsafe.

This project exists because:

  • AI-generated “logic” is rapidly being treated as production-ready.
  • Investors are funding AI frameworks that operate entirely on structure and prompts.
  • Developers deserve to see what happens when you follow that philosophy to its logical conclusion.

ArtificialCast is the result.

It works. Until it doesn’t. And when it doesn’t, it fails in ways that look like success. That’s the danger.

I’ve played with AI in code a few times. There are some tasks it’s very good at, like summarising and explaining (when the developer before you didn’t leave a sufficiency of quality comments). There are some tasks it can be okay at, with appropriate framing and support: like knowing its way around unfamiliar-to-you but well-documented APIs2.

But if you ask an AI to implement an entire product or even just a significant feature from scratch, unsupervised, you’re at risk of rapidly hitting the realm of Heisenbugs, security vulnerabilities, and enormous redundancies.

This facetious example – of using AI as a universal typecasting engine – helps hammer that point home, and I love it.

Footnotes

1 How to cast basic types isn’t entirely standardised: PHP infamously casts the string "0" as false when it’s coerced into a boolean, which virtually no other programming language does, for example.

2 The other week, I had a GenAI help me write some code that writes to a Google Sheets document, because I was fuzzy on the API and knew the AI would pick it up faster than me while I wrote the code “around” it.