An Alternative Arrangement to Cursor Movement in Terminal Applications
I appreciate why many programs support Vim’s navigation control, using HJKL to move the cursor. A classic case of a successful application, built in an earlier time, with an earlier keyboard layout, still making an impact in the software ecosystem today.
But if I may make my pitch, I would suggest newer terminal applications consider using IJKL for cursor movement.
Not only does it emulate the inverted T of the regular cursor characters β a more comfortable arrangement to me β but I find myself naturally resting my hand there due to my typing style, with my index finger on the J and my middle finger on the I. There’s no need to move my index finger over to the H and reposition my other three fingers in a row if I wanted to move around. I always found that manoeuvre awkward, and it’s probably why I eschew HJKL in favour of the normal arrow keys whenever I’m using Vim. I’m moving figures anyway, might as well move them to something more comfortable.
In fact, I like the IJKL arrangement so much that I set it as the default in all the terminal applications I build. It probably won’t work well in Vim β the I key is pretty important β but if you’re building a terminal application, or using one that allows you to change your keyboard bindings, I’d recommend giving it a try.
I’m a little put off by Slack’s instance of hiding things it doesn’t think are relevant. Maybe it’s necessary for large Slack groups, but I’m happy to scroll a bit more if it means finding the channels I interact with infrequently. I’m recall those by where they are, rather than by their name.
I’m still blown away by how good PostgreSQL is. I’ve been writing queries all afternoon and it’s been handling them without breaking a sweat. Granted, it’s not a huge database so I shouldn’t expect anything else. But if we were to use DynamoDB instead, running the same queries would’ve been slower and involved more effort on my part doing the joins in code. So maybe what I’m actually blown away with is SQL.
This service I’m working on, one of the other things it’s responsible for is sending restart commands to this pool of workers. I named the command “restart”, and one of my co-workers was wondering if it was the same thing as resetting the workers. I was somewhat curious about this confusion. There’s no “reset” command, and I know for myself that if someone came to me to reset something, I’d take it to mean that it should be restored to some previous state. Resetting it to factory settings, for example.
But it got me thinking: are the terms interchangeable? I guess it’s not too ridiculous to think so. After all, PC’s in the ’90s came with “reset” buttons, not “restart” buttons. So it might be a little anachronistic, but do others still use “resetting” to mean restarting something?
So I did the most scientific thing that was within my power to do, and created a Mastodon poll:
When I want to describe turning a computer/device off and on again with a single interaction, I'd say that I want toβ¦
100.0% Restart it0.0% Reset it0.0% Either restart or reset (I use both terms)0.0% Turn it off and on again6 people
And the results are overwealming: 6 out of 6 people who responded use the term “restart” over “reset”, with one additional replier saying that they use the term “reboot”.
So maybe this confusion was just a fluke, or maybe I’m reading too much into this. But it was an interesting train of thought, nonetheless.
Spent the morning looking into what was causing the service I’m working on to be performing so badly. Turns out that one of the reasons was just us being stupid but another was actually quite surprising.
Without giving too much away, the service I’m working on takes messages from a NATS Jetstream, and converts them to jobs to be sent to a pool of workers. The workers need to access an API to do the job, and the service I’m responsible for needs to setup the permissions so the worker can do so. This involves calling out to a bunch of other micro-services and producing a signed JWT to grant it access to the API. If the workers are fully utilised, then the service will send any incoming jobs to a queue. When a worker has finished a job, it will get the next one from the queue.
One of the responsibility of this service is to make sure the workers are doing work as much as possible. The pool of workers is fixed, and we’re paying a fixed price for them, so we’d like them to be in use much like an airline would like their planes to be in the sky. So one of the things I added was that the Jetstream handler was to try and send a new job to a worker first, before adding it to the queue to be worked on later. This means that the job could completely skip the queue if a worker is available to pick it up job then and there.
Turns out that this made a huge hit on performance. When the workers are all working on jobs, any requested work sent by this queue bypass logic would be refused, making all the prep work for preparing access to the API completely unnecessary. Time spent on calling the micro-service I can completely understand, but I was surprised to find that a significant chunk of prep work was spent on signing the JWT β a JWT that would never be used. This showed up on the CPU profile trace I took, but it also came through in the output of top
and the CPU status on the EC2 dashboard. The CPU was maxed out at 99%, and virtually all of that was spent on the service itself. CPU idle time, system time, and I/O wait time was pretty much at 0%.
Taking out this queue bypass logic dramatically improved things (although the CPU is still unusually high, which I need to look at), and what I’ll eventually do is add a circuit breaker to the Jetstream handler, so that if no worker is available to pick up the job then and there, all the other jobs will go straight to the queue. There are a few other things we could do here, like raise the number of database connections and Jetstream listeners.
But I’m shocked that signing a JWT was having such an impact. One of my colleagues suggested building the service with GOAMD64=v3
, which I’m guessing would enable some AMD extensions in the compiled binary. I’d be interested to see if this would help.
I generally don’t want to write about things going on at work, but sometimes it helps, and with the self-imposed rule of writing something a day, it’s usually the only thing worthy of comment. Classic case of the topic sometimes choosing you, rather than the other way around.
Seeing a problem at work where the performance of the service we’re working on is just not good enough. Tried bumping up the EC2 instance size and raising the DB connection pool to 20. But the pool won’t go higher than 7 connections.
So it’s either that the connection pool is misconfigured, or the NATS Jetstream client has too few listeners. I suspect it’s the latter. I don’t know what the default is set to, but if it’s a multiple of the number of CPU cores, I’m not sure how well that’ll play with AWS EC2.
So that’s the next thing to look at.
I enjoyed reading this post by Rohan Ganapavarapu. It’s fascinating getting the perspective of someone born after the early internet yet wishing they were there to experience it. The ending’s quite illuminating:
There is neocites, and a small community of people who share this philosophy about the web (and that are relatively young), but I have not met anyone my age, in the real world, that would choose to do something like this.
The majority of people (my age [of 18]) today would think sites like those (and, by extension, their creators) are weird.
I guess, here’s to the weird ones. π₯
Finished work and now I’m having a quick dinner before heading off to a meetup. And I can’t lie: I’ve been nervous all day. π¬
Kind of glad I don’t run a big, multinational corporation.
Scare with care. π
Try-Catch In UCL - Some Notes
Stared working on a try
command to UCL, which can be used to trap
errors that occur within a block. This is very much inspired by
try-blocks in Java and Python, where the main block will run, and if any
error occurs, it will fall through to the catch block:
try {
echo "Something bad can happen here"
} catch {
echo "It's all right. I'll run next"
}
This is all I’ve got working at the moment, but I want to quickly write some notes on how I’d like this to work, lest I forget it later.
First, much like everything in UCL, these blocks should return a value. So it should be possible to do something like this:
set myResults (try {
result-of-something-that-can-fail
} catch {
"My default"
})
--> (result of the thing)
This is kind of like using or
in Lua to fallback to a default, just
that if the result fails with an error, the default value can be
returned from the catch
block. In might even be possible to simply
this further, and have catch
just return a value in cases where an
actual block of code is unnecessary:
set myResults (try { result-of-something-that-can-fail } catch "My default")
One other thing to consider is how to represent the error.Β Errors are
just treated out-of-band at the moment, and are represented as regular
Go error
types. It might be necessary to add a new error
type to
UCL, so that it can be passed through to the catch
block for logging
or switching:
try {
do-something
} catch { |e|
echo (cat "The error is " $e)
}
This could also be used as the return value if there is no catch
block:
set myResult (try { error "my error" })
--> error: my error
Another idea I have is successive catch blocks, that would cascade one after the other if the one before it fails:
try {
do-something
} catch {
this-may-fail-also
} catch {
echo "Always passes"
}
Unlike JavaScript or Python, I don’t think the idea of having catch
blocks switching based on the error type would be suitable here. UCL is
dynamic in nature, and having this static type checking feels a little
wrong here. The catch
blocks will only act as isolated blocks of
execution, where an error would be caught and handled.
Finally, there’s finally
, which would run regardless of which try or
catch block was executed. I think, unlike the other two blocks, that the
return value of a finally
block will always be swallowed. I think this
will work as the finally block should mainly be used for clean-up, and
it’s the result of the try
or catch
blocks that are more important.
set res (try {
"try"
} catch {
"catch"
} finally {
"finally"
})
--> "try"
Anyway, this is the idea I have right now.
Update β I just realised that the idea of the last successful try block return an error, rather than letting it climb up the stack defeats the purpose of exceptions. So having something like the following:
try { error "this will fail" }
Should just unroll the stack and not return an error value. Although if there is a need to have an error value returned, then the following should work:
try { error "this will fail" } catch { |err| $err }
--> error: this will fail
I think Slack’s got an opportunity here to displace Confluence as the “system of record” for work documents. Their Canvas editor is quite good, much better than Confluence’s, and it’s one I really like using. Yet once a Canvas is written and published, they’re ridiculously hard to find again.
Having a central place to browse Canvases and arrange them into categories, much like someone would do in a wiki, would go a long way to making them useful as documents unto themselves, rather than simply “big messages” with short-to-medium-term lifespans.
That’s not to say there’s not a role for such documents. In fact, I wonder if that’s why wikis are always difficult to navigate: you’re mixing documents that have different expected lifespans. System designs sit alongside retrospectives from 2022, which sit alongside the agenda for a meeting next week.
Here Canvases in Slack could be created with the default expectation that the lifespan will be a couple or weeks, or a month, and it’s only those that you explicitly “keep” that would be browsable in this new system. These are the ones you expect to last years and be always kept up to date. The others will still be there β Slack can archive them β but they’ll slowly fade into the background much like the message history.
Anyway, just some random thoughts I had while starting to work on a design within a Canvas and wondering if it should actually go into Confluence.
Discovered that Enya is still releasing albums. Started listening to this one over the weekend. Quite good. π΅
Weekly Update - 20 Oct 2024
Yeah, I know, it’s been a whileβ¦ again. A lot has been happening in life and there’ve been many days that I haven’t done any work on anything. Things are starting to settle down now, although I am expecting a few more bumpy days ahead, so we’ll see how we go with project work.
Cyber Burger
Yeah, I’m getting pretty tired of this one. I’m right in the trough of despair here, where the initial excitement has worn off and I just want to finish it. All the remaining work for it has been defined, which I think helps, and now it’s just a matter of plowing through the tasks.
Recent work done on this is the addition of power-ups and power-downs, which does things like bump the remain time remaining, give the player a “tractor beam” weapon, and clearing the play-area of items, amongst other things. I’m about 2/3 through this one: most of the these power-ups have been implemented, including the tractor beam. After that, the remaining work on this project is adjusting the difficulty curve, improving the main menu, and finally finishing off the website.
Despite how fatigued I feel about this, it has been fun learning how to build for the Pico-8. The expect fidelity of the pixel art is within my skillset, and it’s quite impressive what it offers in terms of audio effects. I haven’t explored the music options, and I probably won’t for this game, but something to look at for the next one (if there will be a next one).
Coasters
This was a bit of a side tangent to get my mind off working on Cyber Burger. It was a long weekend, and I was passively scrolling through the sample of Vintage Logos when I thought about recreating a game that was published in the weekend newspaper we read while I was growing up. You’ll be presented with two images and a clue, and you’d have to guess the word or phrase these alluded to.Β For example, if the clue was “insect” and the images were:
Then one possible answer would be “butterfly”.
The newspaper used stock photos, like the example above, but I was hoping to creates logos for fake companies as they would appear as if printed on cardboard coasters you’d find in pubs. The idea is that a new puzzle would be shown to the user a day, much like the word games you find online.
I coded up most of this in a single day and had a working version by the end of the weekend. I had a very simple Go server which served up a new puzzle every day at 00:00 UTC. The web frontend was mainly HTML, with some JavaScript for handling the player’s guesses. The puzzles themselves were defined in a CSV file which was imported into a Sqlite DB on server startup. This was a strange way of doing things, and was more of an accident as I was deploying this as a Docker container and I didn’t setup a persistent volume that survived restarts. I had plans of adding an admin section to the Go server so that I could design puzzles in advanced.
Fortunately I resisted this as I ended up only producing twelve puzzles. Once the original run was completed, I basically bundled them all on a static site. I was able to keep most of the original HTML and JavaScript as it was, with only the Go backend being junked. It’s still online, and maybe I’ll come back to it in the future, maybe using stock images in place of logos I have to create myself.
So although this was not a long lived side tangent, it was good to be able to get something finished. I was feeling quite dispirited with projects that I was just not finishing or releasing. Doing something small like this, and seeing it through, helped a lot. Maybe that’s the secret in how I can see through something from conception to completion: get the bulk of it done quickly, deploy it as soon as you can, and keep it small. That would limit the types of project I can deliver, but hey, at least I would deliver them.
Other Stuff
I added a few things to Ted, my terminal CSV editor, this past week. Like many of the other features of this project, this was driven mainly by needs that arise at work. I finally bit the bullet and embraced the support for headers. The first row of the CSV file is now treated as a header and is now fixed above the column, instead of the column numbers which were much less helpful. It should, in theory, be possible to read a CSV file without headers, but I hardly deal with those files that it’s probably just better to make headers the default.
I am also exploring ways to do more actions in bulk, such as add columns which are derived from the value of other columns. These are all quite experimental at the moment, and are more or less added in a quick and dirty fashion; just the bare minimum for what I’m trying to achieve. So not much to say here. A few other changes to other projects:
- Nano Journal now keeps the current draft post in browser local storage, so that it’ll no longer get clobbered when you’re redirected to the login screen when trying to post an entry. This was a source of anxiety for me and meant I was less likely to write something off the cuff, so I’m glad this is now fixed.
- Blogging Tool now has a new app which takes a clip videos produced by Pocketcasts β which is currently generated in an Instagram-style vertical video β and crops it as either a square or a letterbox.
So, a lot of little things done this past month. Nothing worthy of a report on it’s own, but still, it’s something.
I was poking around Dave Winerβs Software Snacks β a brilliant name for those β and I stumbled across Little Card Editor. Decided to give it a try.
Archie is no longer with us sadly, so my sister went out and got a new companion for Ivy. Say hello to Rico. π¦
So it looks like Squarespace has been acquired by a private equity firm. I wonder if the new owners will keep buying podcast ads, or if they’ll pull them like Akamai did when they acquired Linode. I get the feeling a lot of shows are relying on Squarespace’s consistent ad money to remain viable.
Iβm going to a meetup surrounding a book that I havenβt read yet. I wanted to finish what I was reading at the time and I figured Iβd have about a week to read this book before the meetup started. I thought I remembered buying it, so when it came time to start reading it, I was a little surprised to find it missing from the Kindle app.
Fearing that I was running out of time, I went to Amazon to buy it again, only to discover that I actually pre-ordered it and that itβs going to be released on the date of the meetup.
So, yeah, feeling releaved about that.
π How to be confident
A great post by Annie Mueller. And pretty much spot on, based on my understanding of how to gain confidence.