The joy of labradors & nonsense software

03 August 2023

It's 9am. As I'm having my morning coffee, my dog begins to violently scratch his head. Sighing, I instinctively haul myself to my feet, grab his back leg until he settles, mutter the words "hey Siri: add a scratch" into my watch, and return to playing with my wordle at the kitchen table (not a euphamism.)

By my count (and I do have a count), some version of this scene played out 249 times over 35 days earlier this year. I have a count because for just over a month my wife and I tracked every time my dog scratched his head using a web app I built specifically for the purpose. What follows is an explanation - to myself??? - of exactly why.


Scratching

Around the start of 2023 my dog, a 3 year old Labrador named Jeremy, started to scratch his face. Constantly.

Utilising his massive back legs - he has impressive muscle tone for a creature who spends 95% of his life on the sofa - he would regularly claw at his face, eyes and ears for up to a minute at a time. He's a lovable little fella but, like the character he was named after he isn't that bright. These episodes led to nasty cuts, most of all in his ears.

This was troubling for us, emotionally and financially (vets are expensive) and was a classic vicious cycle: the more he scratched, the more inflamed his ears got, and the more he scratched them. They say our pets teach us about ourselves and this truly was a humbling lesson in "not repeatedly scratching your fucking face if you have claws."

Our vet told us Jeremy had probably become suddenly allergic to something. Identifying pet allergies is notoriously complicated and time-consuming and nothing obvious about his environment or diet had changed. Apparently, it's common for adolescent dogs to randomly develop allergies.

We had to do something: the dog was clearly suffering and was perpetually mooching about in the cone of shame, banging into doors and generally being miserable.

An image: The cone of shame

The cone of shame

Somehow, we had to figure out what he was allergic to and find a way around it.

One course of action was to undertake lengthy and expensive allergy testing - running diagnostic tests using a range of materials, testing his blood. Try to find the needle in the haystack using a series of literal needles.

Another course of action was to create a web app to track his behaviours: when he scratched, what he ate, what his ears looked like. To gather a data set large enough to try and find patterns and eliminate the allergen that way.

Guess which one of these was suggested by a trained professional vet and which one was suggested by me, someone with a media degree from a former polytechnic.

Needless to say, Me liked My suggestion better. (No diss to vets, you're all amazing.) Or, at least, we agreed with our vet to try and figure it out for a bit, to avoid putting Jeremy through what can be an invasive set of tests.

So I set about designing and building a way to find the patterns.

Not my first rodeo

Funnily enough, I have previous in designing an application to track my dog's behaviour. We got Jeremy in 2020 as a lockdown puppy because my wife and I are incredibly stereotypical millennials. At around 6 months old, Jeremy became incredibly badly behaved almost overnight. This caused strife and anguish as our lovely Andrex Puppy became, to be honest, a prick.

An image: A prick

A prick

People told us that dogs often regressed when they entered puberty and that, in a few months and with consistent training, Jeremy would likely chill out. I was curious to see if this was accurate so, only half as a joke, I created a sort of criminal record for the dog. Each time the dog was badly behaved, my wife and I could enter the offence into a web form I hooked up to a google sheet.

The offences were categorised into Public Nuisance (incessant barking or howling), Theft (he loved to steal anything to get our attention), Vandalism (he'd destroy the things he'd stolen), and Grievous Bodily Harm (this one is self explanatory if you've ever had a puppy).

Spurred on by the lockdown and a shared love of filling out forms, my wife Bethan and I entered each offense for 3 months.

He did eventually chill out, exactly as people told us. We still have his record on file (there's no statue of limitations for dogs) and it's now a lovely glimpse into what was, at the time, quite frustrating. Some of the offences bring back great memories:

An image: A selection of his criminal record

A selection of his criminal record

What's more, at the time, we were able to say - pretty unscientifically - that yes, he was better behaved in May than in March. In a world (it was early 2021) filled with daily covid case figures and death rates, this was a rare positive data point.

It was this little toy app I had in mind when I was designing something to track Jeremy's allergies. I wanted to be able to see him scratch, quickly open up the web app and then log the time of the scratch, alongside logging what he'd eaten that day and any medicine he'd had, to try and build up a data set (statistical significance be damned) we could at least try to use to figure out if certain foods, or activities flared up his scratching.

The technology

I'm going to get technical now, so if you're just here for the dog pics, you can skip to the final section where you'll see the results.

I primarily work in Ruby on Rails, so it was an obvious choice for the backend. But, for the frontend, I was looking for an excuse to dip my toes into the new Hotwire approach. Previously, I might have written something like this in React, sort of by muscle memory.

When I sketched out the app, it was really just a simple form for each day. No need for complicated state management. Also: forms in React suck. Fight me.

I'm not sure when you are reading this, but I created this in early 2023, when Hotwire was still pretty early - but crucially, already a framework default in Rails 7.

Rails has absolutely nailed the 'choose boring technology and focus on shipping interesting products' niche in web development and has managed to ride the frontend insanity wave of the 2010s successfully to a point where the Javascript world is now holding up its batteries-included, opinionated-on-purpose tenets as something to aspire to. The movement away from "SPA all the things" and back towards a sensible balance of Multi Page Applications progressively enhanced with Javascript is one that suits full-stack frameworks like Rails perfectly. The fact that we now have not only gigantically successful JS frameworks like next.js relying heavily server-side rendering, but React itself promoting their use as the default suggests to me that frameworks like Laravel, Phoenix and Rails are validated in their absolutely radical approach of having a server serve HTML.

Putting it together

Holy wars aside, I wanted to try something new and this was a perfect opportunity. The application was very simple: model each day and be able to associate scratches, meals and medicines with the day.

An image: A design sketch

A design sketch

I wanted to have something closer to a Single Page App UX, without too many full page refreshes, and I wanted - frankly - an excuse to try out Stimulus as well. I decided to use lots of forms on the same page, with most of it controlled by Turbo.

I landed on an approach where each model on the page (scratches, meals etc) was handled by a turbo frame that fetched the current state of the main Day object we were looking at, and a form that submitted some change via a Stimulus controller action.

An image: Turbo frames on the page

Turbo frames on the page

Here it is in action

The app in action

The scratches panel is a turbo frame that shows the state of the Day object from the DB showing the number of associated scratches. The plus and minus are links to create/delete endpoints whose controller actions redirect back to the show action that powers the whole frame. When the link redirects the user to the show path, the turbo frame is replaced and the state is updated without any state management on the client.

What's nice about this paradigm is that it can easily scale up to something a little more complicated, like a form that is submitted on change rather than using a regular page submission - like the food and medicine panels. We can progressively enhance the markup with a Stimulus controller that literally says "when an option element is changed, submit the form that wraps that element" and because the endpoint redirects to show the latest state, Turbo takes care of updating the UI for us to show the new value as selected.

Taking it one step further, we can even toggle the view of a form to create a new element, save it, add it to the Day object and redirect to reset the state, all with minimal Javascript.

Adding a new item

As someone who has spent 7 years writing React, this was like the veil falling from my eyes. React has its place, but at least one reason to reach for those kind of UI frameworks (trying to get silky-smooth interactions in the browser) is completely redundant now.

Hey Siri: my dog is scratching again

Speaking of smooth interactions, you know what's NOT smooth? That's right: seeing a dog scratch, finding your phone, unlocking it, navigating to the website, and pressing a small plus icon.

I had some basic UX tricks, like having a route that created a new Day record for the current day so you didn't need to find and create one every day, but I don't always have my phone on me.

However, being the obliging little Apple Fanboy that I am, I do have my watch on me all the time. I wanted to be able to speak into my watch and tell it my dog had scratched, because I wanted to feel like I lived in the future. Not like an inspiring or bold future, more like an intensely trivial and lazy one. The future we deserve.

I'd read something about the Apple Shortcuts app, and looking into the documentation, saw that it's quite easy to make a web request on command: you just give it the address and what the name of the shortcut should be, then when you speak the words to Siri, it'll make the request. You can even tack on loads of suspiciously If-This-Then-That-like actions afterwards.

I thought originally this might be tricky, but actually it was as easy as creating an endpoint in my app that would add a scratch to the current day. Then, I could just hit this endpoint from my Apple Shortcut, and hey presto Siri that little bastard just scratched himself again.

And so, it wasn't long before I was able to see Jeremy scratch, mutter "Hey Siri: add a scratch" into my watch and sleep safe in the knowledge that my precious data set had grown by 1.

An image: Jeremy wore this little collar for a couple of months

Jeremy wore this little collar for a couple of months

Deploying

If I'm shouting out cool technologies I like - and apparently I am - then I want to give a callout to Railway.app which is as close as I've come to the golden days of Heroku.

I remember vividly that earlier in my career as a developer, deploying was something I dreaded. There is nothing worse as a software developer than spending ages on something, finishing it, and then getting completely and utterly stuck trying to figure out what the hell an Elastic Beanstalk is and whether you're going to be rinsed when you come off the Free Tier because your Lambda has some recursion in it. Ok, maybe there are loads of things worse than that immensely first-world problem, but you get my drift.

I've spent years trying to find something as simple as Heroku, and Railway is my favourite so far: they have a very intuitive set up process, adding things like databases or Redis instances is easy and - I can't stress this enough - the fact you actually pay for it is a good signal. It maybe makes it more likely they can focus on their product, and less likely they're going to aggressively flip towards enshittification when the magic money tap runs out.

I've just realised this reads like an advert for Railway. I'm not affiliated to them in any way other than being a happy customer for a while now. But, to be honest, you don't know me (or you do) and me writing that makes no difference. Make your own minds up. And, to the kind benevolent overlords running Railway, I will happily accept some free credits in exchange for shilling for you in the future. Who am I, Kurt Cobain? I don't care.

Anyway. I deployed it on Railway and that was very easy. The technical bit is over now.

The results

If you ducked out of the technical bit, welcome back. After I deployed the web app, my wife and I dutifully tracked as many scratches as we could see, as well as the food Jeremy was eating and any medicine he took.

Let's go over the results:

We tracked Jeremy using the app for 35 days.

In that time, he attacked his head 249 times.

In the first week we tracked him, he scratched an average of 13 times.

By the last week of our tracking, he was averaging 3 scratches a day.

So, something obviously worked! Over the period where we tracked him, we tried a few things, including Piriton (an allergy medicine available in UK pharmacies - GSK if you're reading this I will also shill for you, hit me up), hypoallergenic kibble, and moving him from sleeping in our bedroom to sleeping downstairs.

If we look at the data, there was a huge drop after a few days, which doesn't immediately correlate to any of our changes, but then another sustained positive change started the day after we switched up where he slept.

An image: The total of each scratches each day

The total of each scratches each day

He has since stopped scratching himself almost completely and his ears have almost totally cleared up.

I'm sure it was a confluence of things, but it does seem like moving him downstairs was a huge factor in him chilling out. Unlike our bedroom, the living room where he sleeps has a hard floor. We've since learned that carpet allergies are surprisingly common in dogs.

Jeremy is now happily sans-cone and - for the time being - we only need to give him Piriton rather than any other expensive or damaging drugs. I've retired the app, its job is done.

I'm not sat here thinking that a homespun web app helped Jeremy get better (we just got lucky with the carpet) but it was really cool to build something small that we could use in our real lives. I'm a big fan in using just enough data to get by, or at least to help inform judgments you make. More importantly, I really did enjoy making this little personal piece of software for me and my wife (Jeremy remains oblivious to its existence.)

Lots of your brainpower as a professional software developer becomes tied up in the machinery of business. And outside of work, the internet is littered with people telling you to start a side hustle, to get on that #sigmagrindset. But lots of people get into coding because they want to make cool things.

Sometimes, software doesn't need to be able to scale horizontally, It doesn't need to make money. It doesn't need to be maintainable by a team, and it doesn't always need to use bleeding edge technology. Sometimes its enough to build something because you want to talk to your watch to try and help your idiot dog stop attacking his own face.

An image: U wot m8

U wot m8