March 12, 2025

cdc080274d2c5476f02846d80167f04b.png

Your thumb depresses the microphone button with a soft click. You speak:

"Beep loudly if the pot boils over."

"Send me an SMS when you see the fox in the back yard."

"Count the number of people who walk past every hour and email me a spreadsheet."

"Email me any updates made on the whiteboard."

Watch It For Me is an AI device that can watch things for you. It's a camera you talk to. You press the button and tell it what to do when it sees something happen. It speaks to confirm your request.

Ideally it runs the AI model on-device and doesn't upload anything to the cloud.

I'm not going to make this, but if you want to take the idea and run with it, go for it. This idea belongs to the public domain. If you want implementation or design advice I'm happy to consult. An ideal outcome would be Eric Migicovsky building this!

March 6, 2025

"How long until it's done?" It's the question every software developer dreads. John Carmack famously responded with "when it's done." He knew the inherent complexity of software development. Software estimation is hard.

It's not impossible though. Carmack walked back "when it's done." Scopes are flexible. There is data. We have shipped before and inferences can be made.

Why measure?

Knowing your shipping velocity is incredibly useful. Even a ballpark figure is helpful to make better decisions, set realistic expectations for yourself and clients/bosses, prioritize projects effectively, and generally reduce the stress of the unknown.

Time is the most precious resource we have. As a solo dev and compulsive project starter I have to worry about the opportunity cost of starting a new project. I need to know what I'm on the hook for if I commit to shipping a project. As a freelancer I need to de-risk projects for my clients and myself so they run smoothly and everybody wins.

So I analyzed 36 of my shipped projects. These are my results.

How I measured

  • I used git summary to analyze 36 shipped projects and track the days active and commit velocity.
  • Days active means any day on which a git commit was made in a project. For me this translates roughly to one part time day of dev work (about half a day, from a previous analysis).
  • Commits/Day is just total commits divided by days active.
  • Weeks to Ship is days active divided by 3 (my average part-time days per week on a project).

Notes on measuring

"Shipped" means the software is online, does The One Thing it's supposed to do, and real users are using it. It doesn't mean the issue tracker is empty. I've basically ignored ongoing maintenance for the purposes of this analysis, and I excluded projects that have become long term maintenance projects.

I work on multiple projects at the same time. That's why I work 3 part time days per project on average. I know from measuring hourly invoiced client work that a day active on a project roughly translates to one part time day of work.

One big caveat to note is about LLMs. Almost all of this software was written before I adopted LLMs into my workflow. I'll say more about the impact of LLMs below.

You can certainly argue with the methodology here. Number of commits is similar to lines-of-code - it is at best only loosely correlated with productivity and software quality (and sometimes inversely correlated). It's an imperfect measure. There's a lot of uncertainty in "approximately one part time day of work". In the end the goal was not to get perfect values but rather to get ballpark figures to help me calibrate, plan, and make decisions.

Results

Category Avg Days Min-Max Days Commits/Day Weeks to Ship
Client Projects 45 5-129 6.01 2-43
Micro-SaaS 76 35-106 5.00 12-35
Music Software 52 20-136 3.77 7-45
Games 70 48-84 5.74 16-28
Open Source Apps 26 6-60 4.11 2-20
Open Source Libraries 35 5-101 4.59 2-34

commits_per_day.svg

Note: you can find the "raw data" table below for the full dataset.

Insights

Client projects follow a pattern: While they range from 5 to 129 days, there's a concentration in the 15-56 part-time day range, or about 5-19 weeks of calendar time. So what I can tell clients is an MVP will probably take 2-4 months of calendar time from start to finish. Simple projects can occasionally be completed in under a month. More complex ones might extend beyond 4 months. I can divide client projects into smaller apps and MVPs versus longer projects requiring more maintainance and updates and use that to inform and schedule client work.

Revenue-generating projects take longer: Micro-SaaS projects in particular take an average of 76 days (about 25 weeks), which is longer than client work. More generally all revenue-generating projects took longer on average (54.4 days vs. 37.6 days for non-revenue projects). The added complexity of marketing, payments, and other business jazz, creates overhead that is easy to underestimate. It takes more effort to build things people pay for. It's also more risky than client work. Most of these projects fail to generate much revenue.

Game dev is consistent: My games take 16-28 weeks with a somewhat consistent velocity. That suggests game development is quite predictable, which makes me want to build more games. If I know what I am getting into it makes it a much less risky proposition.

Music software is worth focus: Music software is both fun to work on and reliably generates side-project income. Even small projects can generate some income. I already knew I should focus on music apps, and this analysis backs that up. I can probably build small online music apps in 20-25 days or 8 weeks each and increase side-project profitability. That said I seem to be less productive on music projects judging by low commits-per-day. I'm not sure why that is. Maybe because music apps are more technically challenging to build.

Commit velocity varies by project type: Games and client projects have the highest commits per day (5.74 and 6.01 respectively). I think that's because I'm directly accountable to somebody else with client projects. Games are just fun to work on so it's easier to put dev time into them. As noted, music software has the lowest velocity (3.77) even though I enjoy working on it.

How I ship

There are three main components to my stack that help me ship fast.

  1. ClojureScript. They say programming language doesn't matter but Clojure definitely makes me faster. It's a highly expressive language, which translates into fewer lines of code per feature. Less lines of code, as well as other language features like immutability and functional code, means "faster to develop" and less time wasted on bugs. Then there's the great tooling with a strong focus on iterating during development.
  2. Piku. I ship multiple apps onto a single Linux VPS with Piku. Deployment is a simple matter of "git push". This means I don't waste time configuring things, and it's far cheaper than other options for projects that don't take off (which is most of them). As an open source project it is also quite hackable if I need to do something special. [Disclaimer: I'm a sometime co-maintainer of Piku.]
  3. Project management. You can watch a video I made about how I project manage myself using GitHub project kanban boards. Absolutely key is to constantly manage scope creep. I am the biggest source of scope creep and the technique to manage that is keeping two lists - one for the critical path and one for "later".

A note on LLMs

I performed this analysis before adopting LLMs into my workflow. With LLMs many development tasks are now significantly faster. I don't want to oversell this, as there are definitely many times when you go down an unproductive rabbit hole trying to coax the LLM to actually solve the problem and it becomes a net negative, but when it works it's like magic and you can shave hours of work off a task. One other negative is generated code with errors that passes testing but has hidden bugs.

Probably three things will improve to make those net negatives less likely: 1. Software developers like me will learn when and how to use LLMs and when to avoid them. 2. The tooling and prompting will improve to eliminate the failure modes. Maybe one day the LLMs will know when to say "I'm not going to be good at this task, don't waste your time". 3. Verification and testing methods will get better to help the models stay correct and recognise and fix their own errors.

So it will be interesting to re-run this analysis in a couple of years on projects where an LLM was involved and compare the results. I expect the number of days to build the same type of projects will trend lower.

These days, armed with LLM tooling, I find myself taking on small projects and bits of situated software that I previously would have been too busy for. That trend will probably continue and more custom software will get written in less time with LLMs in the loop.

On the whole, having a natural language machine intelligence at my finger tips when coding is not something I expected in a million years and is quite incredible! I'm very grateful this timeline came to pass.

Conclusion

It's useful to know approximately how long a given piece of software is going to take me to build. I feel more confident in planning now. I can roughly predict how many web apps I will have shipped by the end of the year. I can be more confident in giving client estimates. I can take on some fun projects like games and small tools knowing they aren't going to eat up all of my available dev time.

I highly recommend running the same type of analysis on your own codebases. All you need is a bit of git spelunking with git summary and then some of logging of your hours to get ballpark figures for your own productivity like days-worked-per-week and hours-per-commit.

One final thing I am incredibly grateful for is that I get to write so much software. I'm obviously addicted to it. The ratio of client work to side projects is skewed towards doing my own thing, and I know that I am very lucky to be able to choose to spend my time on what I want. It's a huge privilege.

Raw data

projects_by_days.svg

Category Project Days Commits C/D $
Games rogule.com 48 275 5.73
Games asterogue.com 84 432 5.14
Games SmallestQuest 77 490 6.36
Music Software Melody Generator 49 232 4.73
Music Software PocketSync 20 107 5.35
Music Software BeatGenerator 69 196 2.84
Music Software 8bitmusicmaker 25 82 3.28
Music Software PdDroidParty 136 350 2.57
Music Software speccy 20 71 3.55
Music Software po-loopsync 42 171 4.07
Micro-SaaS hostedgitea 101 485 4.80
Micro-SaaS TweetFeast 60 295 4.92
Micro-SaaS Transcript Generator 35 214 6.11
Micro-SaaS jSfxr Pro 106 443 4.18
Open Source Apps twiiit 6 18 3.00
Open Source Apps slingcode 60 270 4.50
Open Source Apps svg-flipbook 23 63 2.74
Open Source Apps livereload.net 15 55 3.67
Open Source Apps todoMini 29 170 5.86
Open Source Apps makesprite.com 22 108 4.91
Open Source Libs cljs-tiny-slides 5 29 5.8
Open Source Libs cljs-josh 8 31 3.88
Open Source Libs jsfxr 53 200 3.77
Open Source Libs sitefox 101 416 4.12
Open Source Libs bugout 52 178 3.42
Open Source Libs sitefox-payments 28 61 2.18
Open Source Libs roguelike js boilerplate 28 180 6.43
Open Source Libs itwriter 8 57 7.13
Client Projects Client Project 1 33 95 2.88
Client Projects Client Project 2 15 200 13.33
Client Projects Client Project 3 56 182 3.25
Client Projects Client Project 4 5 33 6.60
Client Projects Client Project 5 20 71 3.55
Client Projects Client Project 6 34 291 8.56
Client Projects Client Project 7 71 354 4.99
Client Projects Client Project 8 129 632 4.90

Feb. 4, 2025

So far Clojure is not the best choice of language when it comes to generating code using LLMs. This is probably because it is a niche language and so it is less well represented in the training set. LLMs frequently hallucinate functions that don't exist and other problems when writing Clojure code, and they seems to simply have more trouble writing good Clojure code.

However there may be some advantages to using Clojure when generating code with LLMs. These boil down to Clojure being very concise, and the LISP syntax being easier to verify.

  • Higher information density. Because Clojure is a concise language you can express more information in less tokens. This means the LLM can use less compute for the same outcome. Because it is a LISP there are less superfluous tokens wasted on syntax. So the LLM can be more focused on important information to the task.

  • Smaller context. Some LLMs now have very large context available, but it still seems to be the case that they perform better with shorter more focused context. Once you add too much context they sometimes lose focus on the main task. Again because Clojure code is concise you can use less context space to add the required context and code examples.

  • A consistent syntax and more functional code means easier validation. LLMs perform far better when they are used in a loop where the generated code is linted, tested, or otherwise validated, and then piped back into the LLM with any errors to correct. Clojure lends itself particularly well to this as it's easier to test functional code in isolation and it's easier to parse, lint etc.

In my experience it's possible to get better outcomes when generating Clojure by providing relevant context and code examples. In future we might see code generating LLMs that are explicitly trained on more Clojure code, which would probably also help.

Jan. 31, 2025

We have a tendency to view things as fungible. That's a fancy way of saying "substitute one thing for another." It's how we make sense of the world. We say "that thing is like that other thing, let's compress them into a single category."

Fungible is a term from the world of economics. When we look at a coin with one value on it and a different coin with the same value, we treat them as the same object. We can swap one for the other and get the same practical outcome. This is also true of most products of capitalism. One Mars Bar is much like another and you can interchange them without worrying too much. This is generally good and helpful and makes things work properly. Capitalism biases us quite strongly towards viewing reality as composed of fungible items.

acorns-2.svg

Even in nature we often treat objects as fungible. If somebody says "go and find two acorns" and we find seven acorns on the ground, we can generally pick any two.

No two acorns are the same though. We just pretend they are for the sake of efficiency. They are the same to within an acceptible tolerance. Two coins aren't the same either. If you're into numismatics you know a slight difference between two coins can mean a lot, and no two coins are actually identical after a while.

At the very least, even if you have two objects that are identical to a very high tolerance, they do not occupy the same physical space. Their locations in the universe are different and so are their histories.

These days the bias towards treating things as fungible is increasing. Maybe it's due to the permeation of economic thinking into every day life. Maybe it's to do with the increasingly virtual experience of the human condition. It's got to the point where we even treat other humans as fungible in many situations. The person who served you your coffee. The driver who picked you up this morning. The actual physical human being who clicked the like button when they saw your post on social media.

oak-leaves.svg

It's probably a bad idea to treat humans as fungible. It probably doesn't lead to the kind of world that is fun to live in. It's even more absurd than treating acorns, coins, or Mars Bars as fungible. Humanity is enumerable. There are a limited number of us and each of us is tremendously complex. Each of us evolved uniquely from millions of iterations of change and environmental influence. Every person alive has a unique genetic signature, phenotype, environmental context, consciousness, psychology, and place in history. They're simply not the same. The person who liked your social media post, drove you to your destination, served your coffee. All completely unique and deeply complex entities.

It's true that most of us will be forgotten. It's true that we might not be well known by many people in our time. So what? It's also true that we are physically unique and literally special (definition: "distinct among others of a kind").

In the film Fight Club the main character Tyler Durden says "you are not a beautiful and unique snowflake." I think that's wrong. Every snowflake is unique, every coin is unique, every acorn is unique, and every human is unique. You are distinct among others of your kind.

I'm trying to remember this when I interact with people each day.

Jan. 5, 2025

ClojureScript Tiny Slides is grug-brained presentation software.

  • Write your slides in Reagent Hiccup.
  • Static web app you can upload to any web host.
  • Tiny hackable codebase.
  • No setup or config, just clone the repo to start.

It's built with Scittle so it is developed and runs 100% in the browser.

Source code: https://github.com/chr15m/clojurescript-tiny-slides

I made a video about it.

cljs-tiny-slides.gif

Origin story

Recently I was at Barcamp London and I decided to give an impromptu talk. I had most of a talk written already, but no slides. For the Nth time I evaluated the available presentation software and I couldn't find any which fit my admittedly strange set of preferences. So I did what any sane developer would do and built my own presentation software in the 15 minutes available. 😅 This was only possible because of the amazing tooling available in the Clojure ecosystem.

By the way, Barcamp London is a fantastic unconference and definitely worth checking out!