We’re just one week from the TLA+ workshop! Thanks to everyone who signed up, and if you’re interested, there’s still two slots left!
I also published a new blog post, A Neovim Task Runner in 30 lines of Lua. It’s about a little Neovim script I wrote that can handle tasks like “execute this script on whatever file is in the left window, with whatever flags are in the active buffer’s
b:flags variable. Yes, this is something that I actually needed. I have weird problems.
For the newsletter, the important bit is the “30 lines” part. There’s a lot of reasons why we program, but a major one is automation of manual tasks. For most people, the tradeoff is not between language A and language B but between language A and doing the task by hand.
Now for the obligatory XKCD:
When I critiqued this in the past, I emphasized about how a good automation is worth more than the time saved. It’s not time-efficient to spend a day automating a 5 minutes/month task, but it could still be worth it if you hate the task.
Now here’s a simpler critique: why is it taking you a day?
Why not an hour?
If you can write complete programs quickly, then “writing a complete program” becomes the time-efficient solution to a wider range of problems. An hour is a good measure of “quickly”. That’s about a long lunch break.1
When it comes to writing fast programs, here’s some (not very organized) notes of what’s worked for me. All of this should be roughly applicable to other kinds of programming, too, not just automations.
Fast programming is a distinct skill from software engineering. Software engineering principles don’t totally map over. Things like good organization and clear intent still matter a lot, of course, but a lot of SE theory is geared around how to continuously develop an existing codebase as part of a team. For example, I rarely write tests for my scripts. Setting up tests takes time, and setting up integration tests with fixtures (lots of my scripts are side-effect heavy) takes even more.
At the same time, rapid feedback is really important, even more important than with regular programming. I set up a small “jig” to quickly set up and exec the script in a small way.
Understandability also doesn’t matter as much. The programs are so small that if you stop understanding how it works, you can just rewrite it. And if you need to extend it, you can rewrite it “properly”.
I’m a little torn on how much you should use third-party dependencies in your scripts. Sometimes they’re really helpful, but they add a lot of project overheard, it’s another thing to learn, and many of them have horrible documentation. I guess dependencies that shore up flaws in the language itself are good, dependencies that keep you from needing to learn things are bad. I made one Python tool with click instead of argparse and immediately regretted it. But the same project was only possible because of dateutil, so ¯\_(ツ)_/¯
There’s a few factors here. If we want to write a complete program as fast as possible, the language should be concise and expressive. Concise languages are easier to tweak because you have fewer characters to change. Boilerplate is bad.
Dynamic languages have an edge here. Runtime errors are preferable to static errors. If your program has a type error on executed line 20, then a runtime failure still executes lines 1-19, which gives you information on whether they’re working correctly.
An equally important factor: domain appropriateness. This depends on the task you’re trying to automate. Some languages have really good builtins, others happen to have a great library (see below). Sometimes the best tool isn’t a programming language. I recently had to scrape the street addresses of 100 Google Maps urls and used Power Automate. PA is kinda a crappy tool but it has good browser automation technology, which was well-suited for the task. Also, it comes free with Windows 11. Got the thing done in half an hour.
I also solve a lot of problems with regexes, shell (PowerShell specifically), and the occasional spreadsheet.
Other things of note: A good CLI library is a must. A good GUI framework is nice but less critical. Should be able to run off a single file, and play well with a debugger. Copilot-friendliness is nice if you’re ok using Copilot.
Amortization is when an algorithm has a few expensive operations and a lot of cheap ones, so that the overall runtime is lower than the expensive ops would make you think. f.ex appending to an array is O(1) even though you occasionally need to resize the array, which is O(n), because that happens really rarely. See here for a better explanation.
In our case, amortization is when the time invested on writing a script makes writing future scripts faster. This is really important for developing this skill, so it’s worth discussing how this happens (and how potentially we can accelerate it). First, reducing information lookups. You’ve seen memes like this before:
Also me: Googles array slice
Looking up information in a search engine is slow. It rarely matters in software engineering because most time is spent reading, but here you’re now writing and that adds up quickly. Moving information to a local cache (textfile, comment) is good, Just Knowing It is better.
Also, writing scripts produces snippets you can copy-paste. The first app I made that used tkinter took a while to make, but future ones are faster because I can copypaste tkinter code from older scripts. At the very least, looking up how to do something in a different script is a lot faster than looking up how to do it on Google.
Writing scripts exposes you to the ultra-expressive dark magic in your language, like macros and metaprogramming. You know, the stuff that’s terrible to put in team-maintained codebases? Great in quick scripts, once you learn how to use them well. Magic you make = good, magic dependencies make = bad.
So there’s significant benefits to knowing one language really well, but also benefits to knowing a lot of different tools. I’d say start on whichever side of the spectrum you find most intellectually satisfying, and then branch out to the other side.
In Chicago, the worst days to use the Red Line train are days with a Cubs home game. So I wrote a lambda script to notify me on those days. Will this script measurably improve my life? Yes. If it would have taken three hours to write, would I have still written it? Probably not.
End of post disclaimer: this is all about writing programs quickly, not writing tiny programs. These skills overlap a lot but there’s some differences. Like tiny programs are a great way to practice new languages (I’m doing this for Rust), but it’s also very slow. And with good autocomplete and code generators you can write large programs very quickly.
I think my sweet spot is closer to 2-3 hours, but that’s because I get really ambitious with some of my automations. ↩