I published an app!

I did a thing!

I wanted to name this post “I did a thing™️“, in honor of the person who introduced me to the phrase, Jeremy Clarkson. He often uses that phrase when he does something, especially some easy thing so many other people have done before him, and which are a bigger deal to him than to anyone else. But alas, I am also marketing myself and my apps, so I had to be less stylistic and more professional. I promise, resisting the urge to appear clever was not easily executed.

So as a lot of you already know, on Valentine’s day 2025 I wrote the first line of code for what was to be my first app (iOS app to be specific.) Well, yesterday, June 3, 2025 I released that app, and you can see its web page here: https://KaliTrue.app

But while that’s something that makes me happy, I would like to talk about the process a bit, because while many of you get that accomplishing a goal should bring happiness, the entire process of working toward that goal brought me happiness. As I wrote in a version of this post when I started “… and I’ve been having the time of my life since then, working on that app every day. Yeah, even on weekends… even if only for a few hours. More than once I have found myself glancing at the clock only to find out it was already working on the beginnings of the next day.”

My friends became concerned that I had not taken a break in the first 5 weeks, and that I might “burn out”, so I did finally take a day off. After that I decided that unless there was some pressing reason to work weekends, I would take them off, and for the most part I got better at taking breaks. Well… I took breaks from coding, but I often found myself investigating ideas, looking for new techniques… just exploring the field… but that is my idea of taking time off… more of a shift from one thing to another related thing, rather than a complete break.

April came, and with it some non-coding duties had accumulated to the point where they needed to be addressed, so I was forced to take some time off. It was a bit of a whirlwind actually… broken water heater, half-installed (and leaking) dishwasher, and helping out a friend with a move… April just didn’t exist as far as my project was concerned!

And then in May I got back to it and like I said, yesterday, I finished! My new app, my first app, is now live. It has a web-page: https://KaliTrue.app where you read more about it, and will also find a link to download it for your iPhone, should you wish to do so!

I was surprised by the amount of administration the whole project involved. Forms for taxation, not just for Canada, but for some other countries as well. Forms for export compliance. Documentation on my privacy policy. Declarations about the safety of my app for kids… good news, my app has a 4+ rating so looks like I am good on that, although if your 4-year-old enjoys using my app, an app designed to help calibrate displays for color correctness, then you may want to enroll them in some kind of gifted program and get them off the computer!

Despite the paperwork and related admin, I enjoyed the whole process… and today I am just cleaning up some post-release tasks. Marketing mostly… putting links to my work where I think they might drive people to download it. This weekend I am taking a vacation with my spouse… who is also a small business owner. We’re going to a music festival.

And then, starting next Monday I will begin writing code again. Some code to improve my KaliTrue app, maybe preparing the paid version (don’t worry, there will always be a free version and I will never make previously free features and content paid), maybe working on my next idea…

Ahh, who am I kidding… the dishes are done, festival tickets picked up, clothes for the festival washed and folded, social media posts done… maybe I can do a little work on the next app today…. <Jamie wanders off to write more code>

Thanks to….

I would like to give a special thanks to the people who supported me through this. Family. Friends. Other developers who welcomed me into their clubs, especially Ottawa CocoHeads and iOSDevHappyHour. Educators who share their knowledge, but especially educators Sean Allen and Paul Hudson, the latter being the creator of, among other things, the excellent (and free) online course 100 Days of SwiftUI.

I would like to especially thank Ben, a fellow developer with so much experience I will be honored the day I can call him a peer, who offered to review my code and provide feedback, for free. I expected a simple “yeah, you’re on the right track” at best… but what I got was an itemized breakdown of all my code… professionally reviewed, without judgement, and with an eye to helping me improve. And yes, followed up with an encouraging “you’re on the right track”. I don’t know how to thank you enough, but I will keep my promise to pay-it-forward.

I’d like to specifically thank a handful of people in my local iOS group, Ottawa CocoHeads, but I can’t recall specific names, and the chats on our chat server have long expired. I promise to do a better job in the future to document those who selflessly gave me their time.

And finally, I would like to thank my beta testers, who humored me through this process and patiently tested 21 releases of my app! Five of you were anonymous, but 13 of you were not, and I owe you all a debt of gratitude. Thanks to:
• JP (my patient spouse and financial backer)
• Carmen (my first official–and professional–tester!)
• David (who also helped me with accuracy testing)
• Dean (who became not only my surrogate “average user”, but the idea man I didn’t know I needed. Dean helped me nail down the design of the most visible part of the UI early in the process. Dean also asked a lot of tough questions that led to a better product in the end)
• Doug (who also inspired me to do my very best, especially for my documentation)
• Ira
• Jeff (who patiently suffered through way too many “how about if I did this” questions, accuracy testing, and “should I be going indie?” chats)
• Mark
• Miguel
• Phil (also a great mentor!)
• Richard
• Rob (who could ask for a more supportive brother)
• Russ (not an app tester, but a tester of much of my infrastructure… an equally important role)
• Tom

Thoughts on managing oneself as an independent software developer

When I worked in the corporate world, I would sometimes be sent for training. Mentally, I put the training in two categories:

  1. Training HR wants us to take – includes everything from diversity to hazardous materials handling, but never *directly* related to the task of programming. Usually “soft skills.”
  2. Training to make me a better programmer – from programming languages to support tools.

Let’s talk a little about category 2.

As an independent developer, I now see this category in a completely different light! I used to look forward to such training… an opportunity to make myself a better programmer. My company paid for this training… something I appreciated, but until now I never realized they paid for it in two ways:

  1. Financially – my employer took on the financial burden of my training.
  2. Logistically – my employer took on the schedule hit of my training.

Now that I am independent, I feel the financial hit directly. I always knew there was a financial component to employee training, but it becomes very real when one has to spend money on their own training.

But I am also feeling the “logistical” hit. It is something I was never completely aware of as a corporate developer. My team/project lead took that on. They had to make room in the schedule for this training, for me and every other developer… time that they were *not* developing. Ideally, the training would make the person a better developer, and the schedule hit would be made up in increased productivity… but therein lies a question: how to choose what to train for and for how long, to have a net positive outcome! 

Do you send your employee off to a 2-year program to make them an expert in all things Python (or Swift, or C++)… or do you send them to a one-week boot camp to become better at multi-threading… with multi-threading being the current hot issue in your project.

The answer is obvious… you get your employees trained to fill in the gaps in their knowledge that equate to the gaps in your projects’ immediate needs. And if you’re a good manager/leader, you develop a feel for this. You identify the gaps that would most benefit from employee training, and you also identify the employees who will bring you the most value should they receive said training.

This is a dance I am learning now, but the dance floor is a bed of hot coals! As project lead, I am pushing for delivery of that last 10% of the code to get to v1.0, and in so doing, I am seeing gaps in my team’s skillset (this past 2 weeks it’s been Swift 6 new concurrency model) and having to determine how much I need to improve the team’s comfort with that skill to move forward with the project. Except I am also the development team… and I love to learn… so when I sat down to better my knowledge of Swift Concurrency (especially in light of Swift v6 improvements in that area), I wanted to know it all.

And so it comes back to a recurring theme for my new role as an independent software developer… how do I fill the roles that used to be provided for me through the corporate structure? It’s meant I have had to learn to be an architect, a team lead, a project lead, a manager, a technical writer, all while continuing to get better as a developer in a new (to me) language on a new (to me) platform.

It’s been fun for sure… but it’s also helped me appreciate just how much goes into the creation of a software product. It’s a life cycle, and I continue to learn.

Why do Jamie’s apps use American English?

The KaliTrue app is the first app release by me, Jamie Cashin, who happens to hail from Canada. So why then does the app contain American English terms such as “color” where one might expect a Canadian to have used Canadian English terms such as “colour”?

Well, this is a decision taken by me for many reasons and since you’re reading this article, you must be interested in my thinking. If so, please read on!

For most of my career, I worked in the embedded space, meaning no one could directly see the results of my work. If you’re not familiar with the term embedded, let me simplify it for you. Most of the software I have written (up until the spring of 2018) would inspect values in hardware, or react to changes in values in hardware, and then take some action based on that. For example, I might have written code that monitors the temperature reported by some sensor, and when that temperature drops below a certain value, my software modified another value to tell a heater to turn on. There was nothing displayed to an operator… the software I wrote lived deep in the bowels of some machine.

My work was just part of a larger collection of software, shared among many developers. To make it possible for many people from various backgrounds to work efficiently on the same software base, it is common to standardize on English as the common language, and in particular, American English. If you were not aware, computer languages are just that… languages, and they enable programmers to write human-readable code, when a computer then translates to its own internal language, a set of specific instructions which the computer is then able to act upon, or “execute”, as we say in the biz.

So let’s say we have some code that when things are cold, it turns on a heater and makes a lamp red, and if things are normal it turns off the heater and makes the lamp green. This code might look something like this:

let currentCoolantTemp = getTemperatureOfEngineCoolant()
if currentCoolantTemp < 5 {
  coolantHeater(on)
  setLampColor(blue)
} else {
  coolantHeater(off)
  setLampColor(green)
}

Do you see the setLampColor part? Yeah, one might expect a Canadian to have written that as setLampColour, but because the software development team normally consists of people from all over the world, American English spelling is preferred to avoid confusion. It also makes it easier to find things in the code later… you only need to search for one term such as color or neighborhood, rather than color+colour or neighborhood/neighbourhood.

In 2018, through a fun misunderstanding I should blog about some day, I found myself in what’s known as the UX domain. UX is short for User eXperience, but you may have heard the related term GUI or UI for (Graphical) User Interface. Working in the UX space was a whole new way of thinking for me! For the first time in my career, actual people would see the results of my code! For example I might have written software that presented a screen to a driver, which would let them change the color of the lighting in their vehicle. The screen might therefor present text to them like so:

Your lighting color is currently Red

Again, the underlying software might store the current colour internally in a variable (a storage bucket) named currentColor but that is independent of the spelling presented to the user! And yet again, the value was presented with the American English spelling, color.

Now the reason for using American English for presentation to a user is probably easier to understand. The first version of most apps is written to present things in a manner palatable to the largest number of users (customers) and this usually means American English. Sometimes this ends up being the only version available, and Canadians, Brits, and the French all have to understand what “color” is.

Now it is possible to localize your app… where you as the developer provide translations for all the text that gets displayed to the user, into other locales. This can mean a simple variant on English, such as Canadian or British English (colour), or other languages entirely… couleur for my French friends!

And so we’re back to the original question… why will my first app be released initially American English, and why will all my future apps do so as well? Because of “market share”. You see it’s a simple fact that most of the people who will install my apps are American… so it’s wise for me to give them what they expect. Especially when non-Americans are so used to dealing with American English. This is where my customer base is… it pleases the vast majority of my customers.

So that answers the question… but you may now be wondering, well, what about my Canadian, British, French, Ukrainian, Mexican, Australian… and all other non-American customers? Don’t they deserve a more localized experience? Well, yes, of course they do! But the truth of the matter is I have to balance my development time between new app development, new feature development on my existing apps, bug fixes, and localization, and all these things take time! As unpalatable as it seems to you, and I agree it’s unpalatable, I will usually have to defer localization of an app to work on what pays the bills: new apps, and new features or bug fixes to profitable apps.

So there you have it… I have not “given in” to Americanization. Instead I have recognized the need for a small dev shop to prioritize work in order to maximize profitability. It sounds shallow, but as much as I truly enjoy the process of writing software, I can only do so if I continue to pay the bills, so localization (unless you’re willing to pay me for it!) takes a back burner.

Take care, and thanks for reading!


†Speaking of paying me, yes, I am available for hire or contract, and yes I am willing to talk to you about collaborating on your app idea! You can message me from my LinkedIn profile and you can find my socials linked from JamieCashin.com

I did it… I used AI to write code :(

My sad story

Sigh.

I feel so dirty.

I finally did it.

Yes, I used AI (ChatGPT) to write code.

Still with me? Good, because I would really like to explain myself!

So I have this backup drive that I’ve just been dumping files to for years, and I wanted to re-purpose the drive, possibly moving some of the content into the cloud. Because the drive had become a dumping ground, it contained many duplicates, i.e., multiple copies of identical files, so my first task would have to be identifying and subsequently removing duplicate content. While I knew this task would be massive (there’s 2.4TB of data), I am a software developer and so it was natural for me to write scripts to simplify this task.

The first thing I needed to do is—for each file on the drive—compare that file to all the other files on the drive to find any that were exact duplicates. Directly comparing the files to each other would be very time-consuming, so I needed to find a more efficient way. If only I could—for every file—generate some kind of fingerprint that —for all intents and purposes—could uniquely identify the content of said file.

Well, fortunately, there is a command-line tool md5sum that, given a file path, calculates a 16-bit value based on the content of the file. This 16-bit value, expressed as a 32-character string, behaves just like a fingerprint, uniquely identifying the file content such that two identical files (clones of each other) will generate the same md5sum, and this happens irrespective of filename!

Now there are other ways to generate this fingerprint (sha, for example), but I needed something that was extremely fast, while still having a low probability of collision, i.e., two different files having the same md5sum value. Well, md5sum is not only very fast, but the generated fingerprint has a near-zero probability of collision at just 1 in of 2^128 (see more at the end of this post).

Despite being fast, md5sum does have to perform some calculations based on the entire contents of each file, and all 671,406 files comprising 2.4TB resided on a very slow external drive, a 5,400 RPM physical disk, connected via USB3. Generating the file listing the md5sum and filename of every file on the external drive took a long time… I started it around 3 p.m. one afternoon and it was still running when I went to bed, but it was finished by the next morning.

Now I could take the results, a single file listing the fingerprint and pathname of each file on the external drive, and for each fingerprint, check if there were any matching fingerprints, and if there were, record the names of the duplicated files. I was able to implement this as a bash script, complete with progress reporting (so I could watch it run), in about 15 minutes, including testing it against a subset of data, manipulated to test all execution cases. Armed with my new bash script, I ran it against my data and while I was happy to see that it worked, I was sorely dismayed by its performance. It was taking nearly 3/4 second to process each file… that would take more than 7 days to finish!

Now I will admit my bash script could stand some improvement, but I knew that python could easily outperform bash task-for-task, so I decided I needed to rewrite my script in python. My relationship with python is interesting. Unlike other languages where I have tried to master, python has remained as something I feel I am still learning… when tasked with writing something in python, I take more of a “let’s get it to work” approach. This comes from the disposable nature of much of the python I have written… as with this exercise, I often write a python script to resolve a particular issue, use it, and then have no need for it any further. Sure I have written many “designed” or “engineered” python scripts, but more often than not, python is little more than a way to create a disposable tool.

And now I can get to the point of this post… here I was, with a bash script, that while working fine, would take days to complete its work. I knew that even though it was disposable, it was still worth the effort to write an equivalent python script to get my work done in a more timely manner. So I fired up vim and started to write the equivalent python script, and then it hit me: why not ask ChatGPT to take my bash script and convert it to python. It was worth a try, right?

I opened ChatGPT, typed “Convert the following bash script into python” and then pasted the bash script into the prompt, and pressed ENTER. In less than 15 seconds I had ChatGPT’s response… which I copied and then pasted into a file on my local drive, naming it doit.py. I expected I would have to do some work to get it working, confirmed by the fact that ChatGPT didn’t suggest the #!/usr/bin/python3 hash-bang to make the file executable natively rather than invoking python on the script. I fired it up and…

It just worked! No debugging… no futzing with it… it just worked. And it was much faster than the bash equivalent!

Now I will admit that yes, while it was faster, about 10 times so based on my wet-finger profiling of it, it could still benefit from some tweaking to make it even faster. But that was not the point of the exercise. The point was, could ChatGPT generate a python script from my bash variant, that was at the very least a good start? Yes, in this case it certainly could. Does this mean I will always have this trouble-free experience? Maybe not, especially as this test used a very simple bash script.

But I decided to see if ChatGPT could do better. I added “Can you rewrite it to make it faster” and it converted my script to use what’s known as a dictionary (something I was surprised to notice the original did not do). The new version finished in 0.68s. That’s quite an improvement over 7+ days!

But was this experiment a success? Yes, it most certainly was, and it motivates me to use this technique again, at least for my own personal needs.

Would I use this in a work environment where I am being paid to develop code? At this point, probably not, but probably not for the reasons you’re thinking. The main reason I would be uncomfortable using this in a work environment would be down to intellectual property ownership. I’m not going to take the intellectual property of my employer and paste it into ChatGPT… but for use at home, this capability is promising. My reticence to use this for commercial code would be mitigated if my employer had a locally running instance of some coding AI model, but that’s a topic for the future.

My experience with using ChatGPT to generate code, albeit driven by debug code I had written already, is very positive. I’d be interested to see how this is being used by others so feel free to let me know if you’ve done similar things or your company has embraced this idea.

Bonus

Bonus – how reliable is md5sum at generating a unique fingerprint for a file?

How confident am I that the 32-character value md5sum generates will be sufficient to uniquely identify the contents of a file? Well, the probability of any two hashes accidentally colliding is roughly 1 in 2^128.

But what is 2^128? Well, it’s a lot… specifically, you would have to compare 340,282,366,920,938,463,463,374,607,431,768,211,456 unique files for it to be likely that just two would have the same checksum!

For those wondering, 340,282,366,920,938,463,463,374,607,431,768,211,456 in words is:

  • Three hundred and forty undecillion,
  • two hundred and eighty-two decillion,
  • three hundred and sixty-six nonillion,
  • nine hundred and twenty octillion,
  • nine hundred and thirty-eight septillion,
  • four hundred and sixty-three sextillion,
  • four hundred and sixty-three quintillion,
  • three hundred and seventy-four quadrillion,
  • six hundred and seven trillion,
  • four hundred and thirty-one billion,
  • seven hundred and sixty-eight million,
  • two hundred and eleven thousand,
  • four hundred and fifty-six.

Credit for the spoken form of 2^128 goes to https://www.techtarget.com/whatis/feature/IPv6-addresses-how-many-is-that-in-numbers#:~:text=So%202%20to%20the%20power,numbers%20without%20resorting%20to%20math.