Chris Hannah


Technical Debt Sounds Dumb #

Kam Lasater, wrote a great piece on technical debt. Primarily explaining how stupid you sound when most people try to explain technical debt to businesses-minded people.

When we use the term technical debt with non-technical business colleagues, they assume, that technical debt is analogous to financial debt. After a few minutes of discussion they are usually relieved to find that there is no actual money problem. Then after not seeing any tangible sign of a fire in the movie theater, they will then conclude that that we either don’t know what we are talking about or we are using the wrong terms or both. Think about their context, how quickly would the CFO get fired if they claimed “we have a lot of debt” but couldn’t produce a balance sheet with lenders, amounts, interest rates and terms?

The article goes into a lot more detail on what the problem is, and also the ways you can explain it better. But essentially, the trick is to explain it in ways that people can understand:

Companies and business leaders don’t care if jobs are hard or annoying or take longer than they “should”. The difference between a user story taking a day or 3 days is negligible compared to its business value. Companies and their leaders care about revenue and costs. They care about customers and growth. They care about time to market. If we want to have our non-technical colleagues listen and act, we need to either improve our use of the financial terms they understand or we need to translate our message into business outcomes that they do care about.

I’ve seen this myself where I work. Every so often it’s regarding technical debt, but typically, I think it’s when people aren’t aware of the context in which they’re explaining something. It’s something I definitely had to get to grips with as well, learning how to talk to various people, what they understand, what they’re interested in, and overall, what matters to them.

This may sound rude, but I think sometimes us developers get a bit stuck in our own world. We tend to use a bit too much jargon, arguing over the stupidest of problems, and not really focussing on the wider business perspective. For example, the value of the product doesn’t get better if you use a different code style.

One thing that helped me at work was to spend time with people like product managers, business leaders, or anyone else non-technical that I had to explain things to. That way I learned their perspective, and adapted my wording to fit. It’s only going to be better for both parties if you can get things across clearly. It avoids unnecessary confusion and it saves time.

Working on Bulba #

Over on my development blog, I’ve been writing about my recent project, Bulba (a static site generator that powers said blog).

Specifically, I wrote about how I write code, track features, and release new versions. So if you’re interested in reading about that, Bulba in general, or just want to see what sort of website Bulba is currently capable generating, check out my development blog.

A Few Things I Use the Command Line For #

I’ve noticed myself using the command line a lot more recently, at home, and work, so I thought I’d share a few of the little tools and handy commands that I use on a day to day basis.

Note: I use ZSH as my shell, with oh my zsh, so they may differ slightly if you’re using something different.


The most helpful commands that I use have to be aliases for the most minor commands. But because they’re used so often, it saves so much time.

The majority of them are for two things - moving to common directories, and launching applications.

Here are a few examples of directories I have set up with aliases:

That’s just a small snippet, but usually, I have most projects set up with a very small alias. But even if I don’t have one set up for each project, I’ve got one that puts me at the root of my developer folder anyway.

As for applications, I’ve got a few that I use a lot:


Being a developer, I use Git quite a lot. And that is where oh my zsh comes in handy, as it comes with a huge number of aliases for common Git commands. Here’s a great cheatsheet.

Here are the ones I use the most, and also what the full command is:

I’m aware that those are pretty minor commands, but they’re so much easier when they’re just one two or three letters.

Also, oh my zsh does come with an alias for committing changes with a message, but it’s gcmsg and that’s longer than just using gc with the -m option.

The most used Git command I use though has to be a little ZSH function I made myself:

function gacp {
    gaa; gc -m $1; gp

It stands for “git add commit push”, and as you can probably tell, it adds and commits the following changes, with the supplied message, and pushes it to the remote repository.

Most of the time I’m doing stuff like this:

gacp "JIRA-123 fix tests"



I don’t use these a lot, but I do have a few aliases to update various websites. They basically use the scp command (secure copy) to transfer files from a local directory to a remote server. Guide.

This isn’t exactly what I have, but they all follow this rough syntax:

scp -r /Users/chris/website user@

This will recursively upload the contents of a local directory to a remote server. I use this whenever I update changes to my blog theme.

HTTP Requests

Whether I’m working on a mobile app or REST API at work, I’m usually testing various requests throughout the day. And while I sometimes use a tool like Postman, especially when I’m building a collection for QA testers, I do find it a bit cumbersome sometimes. So that means I end up resorting back to the terminal.

I’ve seen a tool called httpie which does seem to be quite good, but I’ve found curl to be good enough for my uses.

Tip: If you’re stuck with the syntax and don’t have time to wade through documentation, I’d recommend using a tool like Postman to build the request, and you can then export the curl request.

Most of the time I’m just performing GET requests, so the syntax is simply:


If you need just the headers of the response there’s the -I option, and if I want both the headers and body it’s a lowercase -i.

Environment Variables

Usually, I’m using the command line because I want to test quite quickly, and with slight tweaks, so I find making the command as short as possible helps with this.

The first one for me is to use environment variables. So for example I’ll use one for the base URL of the API, and usually a few for any variables that need to be in the path, especially if these are user account numbers, as it makes it a lot easier to quickly test different scenarios.

This means that a request like this:


Can be made a lot shorter by using two environment variables:

export base=
export account=2a3e4832-14e6-430d-8c34-748f4626e864/transactions

Which means it can look like this:

curl $base/account/$account/transactions

The biggest benefit I find is that allows you to edit the command much easier.

Using Files

Another tip I have for curl is that if you have a bunch of headers that you need to use, then it helps to have these stored in a file.

You can do this by using the -H command followed by @ and then the filename. For example, this command will read the headers from a file named headers.txt:

curl -H @headers.txt

The header file needs to be in this format:

Key: Value

So something like this would work:

Content-Type: application/json
Authorization: Bearer [token]

This is especially handy for me as most of our APIs at work require various authorisation tokens that can be quite large.

You can also use other options to use files for storing the body of the request, but I’ve not had much experience of that, so I’ll have to defer to google.


This goes hand-in-hand with making HTTP requests, in that the responses are usually JSON. For that I use the JSON processor, jq.

Most of the time, I’m just using it to “beautify” data from a curl request, so I pass through the response to jq by piping the output from curl to jq like so:

curl | jq

What that does is take the response from the curl request and output a pretty printed version of it.

But you can also use jq to parse the JSON response and pick out certain fields.

So for example, a request to my blogs JSON feed at will return a fair bit of JSON data. Something like this:

  "version": "",
  "title": "Chris Hannah's Dev Blog",
  "home_page_url": "",
  "feed_url": "",
  "description": "A devlog by Chris Hannah",
  "author": {
    "name": "Chris Hannah",
    "url": ""
  "items": [
    { ... }

But say I only wanted to read the author object, I’d just need to use this command:

curl | jq '.author'

Which will return just this:

  "name": "Chris Hannah",
  "url": ""

There’s a lot more it can do as well, and I’d recommend checking out these examples.

I’m sure there are tons of other resources that go into far more detail on what you can do with the command line. But I thought I’d share a few things that I use it for, just in case it might prompt others to find some ways to save themselves time!

“Built using Bulba” #

Two announcements. Firstly, I’m building a static site generator. And secondly, that I’m using said generator to power a new blog of mine.


That’s the name. My new side-project, which is a static site generator, built using Node.js, that can transform a few Markdown files into a static site. It’s still early days now, but it’s already got paginated index pages, archive, about page, JSON feed, and of course, a page for every blog post.

I’m in no way a Node.js or JavaScript programmer, so I can’t always be certain that my code is the best. (Nor can I say that it’s the worst). But I think it will be a fun project to work on, and maybe in the future other people can use it.

You can find Bulba on GitHub and NPM.

Development Blog

To both demonstrate the features of my new tool, and to also share the progress of it’s development, I’m using Bulba to power a new development blog. It’s got a pretty nice url as well: .

Right now, I’m using it for Bulba specific updates, but I can imagine that in the future I may use it to write about other projects I work on too.

Earlier today, I published a post, “Introduction to Bulba”, on my new blog, where I introduce Bulba in detail, showing how it works, what features are currently implemented, and also what I’m working on next.

Goodbye, Qwiki #

Unfortunately, I’ve decided to remove Qwiki from sale.

If you weren’t aware, Qwiki was a Mac app that placed Wikipedia in your menu bar. It was pretty simple, you could search for a page, view a page, and there were a few methods to export links from the app.

Qwiki was first released in June 2016 and received updates until November 2019. At that point, I was relatively happy with the app, since it was only ever meant to be a minimal way to quickly search Wikipedia.

However, as time has gone on, the codebase has become stale, and the app, in general, doesn’t feel at home on the more recent versions of macOS.

I’ve felt for a while that I shouldn’t be offering an app that isn’t being maintained, but my thoughts were that while people still used it, there could still be more that could get value from it. But after receiving a few support requests asking for Catalina, mainly around the text appearing too small, small icon resolution, and a few more things, I can’t continue to make that excuse anymore. So therefore if I have no plans to ever update the app, I can’t ask people to pay money for it.

I toyed with the idea of just making it free, but I still think a free app needs to be of a certain standard, and to a point, maintained.

This means that I now only have one app, Text Case, although it is available on iOS, iPadOS, and macOS. I do have plans to work on a new project this year, but nothing is in the works just yet.

Having an App Featured in the Mac App Store #

At the start of last month, I released a major update to my app, Text Case. It got some good reviews, and that helped boost sales, which I’m very grateful for. But as you would expect, after a week or so, the initial surge ended, and it went dropped to a stable level every day.

But I was looking at the app analytics section App Store Connect one day and I saw a huge amount of impressions on the macOS version. It was around 1 million impressions, where usually it would be around a thousand a month.

In the end, it was around 3 million:

I immediately thought it meant that my app must have been linked somewhere popular, or that it was featured in the App Store. Turns out it was the latter. Text Case was in fact featured in the “Apps and Games We Love Right Now” section.

I started thinking that this could mean a sudden increase in downloads because so many people would know about the app.

But as it turned out, there may have been a small increase, where the sales didn’t drop off as fast as it would have done without the feature, but it wasn’t what I was expecting.

Below is the number of units during the same period as the above impressions (February):

After a while thinking about this experience, I’ve come to the conclusion that this just shows how much word-of-mouth and direct recommendations work, compared to simply being visible on the app store. If a few reviews can have the same impact as millions of impressions, then they must be pretty effective.

For a while, I always imagined that simply being featured could push an app to get a large number of sales. Maybe it’s because Text Case is a particularly niche app, but I think this proves that directly reaching people that would benefit from your app is the best way to grow sales.

Maybe this won’t surprise anyone, but it’s a perspective that a few people may find interesting, so I thought I’d share.

Text Case 2021.1 Beta #

After quite a long time of development, the major update to Text Case that I've been working on is finally ready for a public beta.

This update changes the focus of the app from simply using built-in formats, to focus on user-created flows. A flow is a collection of formats, which together can become a more complex text transformation.

Along with flows, 6 more complex formats have been added. The complexity in them is that they support parameters. So when you add them to your flow, you will need to configure them. The new formats are:

As they are more complex actions, they have specific Shortcuts actions, Replace and Remove. You can obviously also access them if you add them as part of a flow, as flows are now accessible via the Shortcuts app too.

There's a few extra tweaks to the design, and also some configuration options for the Share extension. But that's essentially the main changes in this update. I'll write a much more comprehensive post when the update is finalised and ready to download from the App Store.

If you want to give it a try, you can join the TestFlight beta. Any feedback or ideas are completely welcome.

Having Some Fun With Continuous Integration #

I had an interesting day at work today, as I was configuring a new project work with our CI server, and have things like Unit/UI Tests in a readable format, and also convert the code coverage into something that could be stored along with the build artefacts.

Just for some background, we use Bamboo as a server, and I’m pretty limited with what I can actually configure myself, without getting someone with higher privileges. So I try to work within my limitations, and see what I can come up with.

I use Fastlane as the main solution to manage the whole process. And that means I can use the scan and slather commands to do the heavy lifting for the testing/code coverage. The way I had to integrate it in our CI server was reasonably simple. The test results were handled by setting the output type to junit, and then adding a simple JUnit Parser task on Bamboo. The code coverage was slightly more complex, as it needed me to run a python package that converts it into a “Clover” format that Bamboo could understand.

What was more tricky, was getting this data nicely formatted when it was sent to our Slack room. The previous build plans all had notifications handled form Bamboo, and it just gave a short message with the number of tests that passed/failed. I wanted more insight this time though, as I knew the test data was available, and also that I had code coverage being generated. I decided that the simplest (maybe it wasn’t in the end) solution was to just find a way to read the information from the .xml files, and send a custom message to Slack as part of the Fastlane process.

What I ended up with is a kind of monstrous-masterpiece. In Fastlane I had the Slack command being called with some basic information about the build, such as the branch, project name, and whether it passed/failed. But to get the results of the Unit/UI tests, I thought I’d use grep to find the line in the junit file that had text like “tests=100 failures=0”, I then used sed to clean up the surrounding text, and had the final output as “Passed: 100, Failed: 0”. The code coverage was slightly harder. I used grep and sed again in the same way to find the total code coverage, but it was formatted like “1.00000000”, and I wanted a percentage. So I piped that through bc with a small calculation, and they’re not formatted as a percentage with two decimal spaces.

Then with some magic of environment variables, I added two build-specific URLs to the message payload. One for the build details, and the other which linked directly to the code coverage report.

What I ended up with was something like this:

I’m not sure if all of that is relevant for each build, or if I’ll have to include some other things I’ve forgotten about. But what I can say, is that it was really fun to come with all of these little scripts that come together with something so simple at the end. And it’s quite likely that no-one else seeing the messages will have any idea the lengths I went to to make everything appear so simple.

Starting Work on Text Case 2.0 #

As you may have already seen on my Twitter, or in my journal entries, I’ve started to work on the second major version of Text Case, 2.0. The major changes will be to the user interface, so I want it to be slightly more colourful, fit more in what I see as the latest design language Apple has set out in the Shortcuts app, and also have the formats structured better.

The project started with me making a list of all the things that I will need to implement for it to be level with the functionality of the current version. Here’s that list:

I started working on the most important section of the app, the formats list. Over the past few days I’ve been building up the style similar to the Shortcuts app, so instead of being simple white boxes that contain the formatted text, they’re more colourful and even have a slight gradient to add a bit of depth (I’m planning on experimenting with a small shadow as well).

So once the list was working, I added the core logic from the current version and made the formats work. I did adapt it slightly though, as it now groups similar formats together, which I think makes the app look a lot tidier. This change means that when I add the reordering feature, it will most likely me limited to reordering the groups rather than individual formats. You’ll still be able to hide any you don’t want to see though.

Then I added the input field. It’s also a bit cleaner, and fits with the new style. But it has essentially the same capabilities as before. I plan on investigating importing text from a file, and implementing drag and drop, but I think that’s supported automatically.

After I had the list displaying, input working, and the text being formatted, I worked on the interaction with the resulting formatted text. I’ve had a few bits of feedback in the past saying they would appreciate one-touch copying, and now I’ve added it! So you can simply tap any formatted text in the app, and you’ll get a nice alert at the bottom showing the exact text you’ve copied. Or alternatively, you can still tap and hold on formatted text to bring up the contextual actions, which are the same as before, copy and share.

The next step from here will be to start working on the settings section of the app, as that also allows me to test the rest of the app in different scenarios much easier. I’m already planning two changes to the settings in this new version. The first is changing the idea of an accent colour to a theme, as I want the format groups to control the colour. But I also appreciate that a light and dark theme is a minimum. The second change is custom app icons, they may be a basic selection, but the app no longer has a “main colour” so I’d like to give a few options.

If you want to stay up to date with the development of Text Case 2.0, You can find more regular content on my Twitter, brief updates on my journal, and I’ll still post any major progress here.

Text Case 1.3.1 #

Another update to Text Case has just hit the store!

Just a small one this time though, to tie up a few things, before anything big can be planned or worked on. In fact it can be boiled down to three things:

It’s not an extravagant update, but then again, they can’t all be.

Find Text Case on the App Store.