As a software developer and architect, I’m expected to keep track of what I work on. Regularly.
Unlike coding, designing systems, or thinking about architecture, this is one part of my job I genuinely hate. As a result, I tend to postpone filling in my work log until the point where I’m staring at a couple of empty days, trying to reconstruct what on earth I was doing on Tuesday afternoon.
Over the years, I’ve tried a few applications that were supposed to help. Timing, for example, is an impressive piece of software and can automatically tell me where my time went. But I always found myself missing something. What I really wanted wasn’t a sophisticated automatic time-tracking system. I wanted a very simple tool built around the way I work: pick a task, start working, record the time, add a short note, and later have something useful to copy into the work log I’m obliged to maintain.
It was one of those ideas that was too small and too personal to justify spending weeks developing it — and at the same time too specific for an existing application to quite fit.
Apparently, it had to wait for coding agents.
My native habitat is Java. I can happily spend my days there, but Java would hardly be my first choice for building a small, native macOS application. So instead of first becoming a Swift expert and then writing the application, I decided to learn just enough Swift and SwiftUI to be dangerous and let a coding agent help me with the rest.
I started working on Tomatoro together with Claude Code.
The first version did almost nothing: create a task, start a timer, and remember how much time I had spent on it. That was useful enough to start using it myself — which, unsurprisingly, immediately produced ideas for the next iteration.
Then another one.
And another.
A countdown appeared alongside the stopwatch. Then manual work-log entries. Notes. Task history. Daily summaries. Menu-bar controls. Idle reminders for those remarkably frequent occasions when I get absorbed in something and forget to start the timer in the first place.
What started as a tiny experiment gradually turned into a small macOS application that I actually use for my everyday work.
What Tomatoro does today
Tomatoro is still intentionally a small application. Its purpose hasn’t changed much since the first iteration: make recording my work require as little effort as possible.
The basic unit is a task. A task can have its own description, and every individual work record can have a description as well. This turned out to be important: knowing that I spent three hours on Project X isn’t particularly helpful when I’m filling in my work log two days later. Knowing that I spent those three hours on reviewing the persistence model, debugging the deployment, and discussing the API with a colleague is considerably more useful.
Time can be recorded in several ways.
There is a regular stopwatch for the simple start working now and tell me later how long it took case. There is also a countdown timer, inspired by the Pomodoro technique. I often know that I want to dedicate, say, the next 30 or 60 minutes to a particular task. Tomatoro lets me start such a session and notifies me when the allocated time is up.
And because real life doesn’t always cooperate with timers, a work record can also be added manually afterwards.
The menu bar is where most of the work happens
A time tracker that requires constantly switching to its main window would quickly become another application I’d stop using.
So most of Tomatoro’s everyday functionality is available directly from the macOS menu bar. The status item shows whether a session is currently running and displays the elapsed or remaining time. From its menu I can start a task, choose between a countdown and a stopwatch, pause or resume the current session, stop it, or add a manual record.
The main window is there when I need to manage tasks or inspect things in more detail. It doesn’t have to sit in front of me all day.
Reminding me to actually use the time tracker
There is an obvious weakness in any manually operated time tracker: it doesn’t track anything when you forget to start it.
And I do.
So Tomatoro has idle reminders. If nothing has been tracked for a configurable amount of time, it can send a macOS notification and indicate the idle state through its menu-bar icon.
The behaviour is configurable. I can choose how long Tomatoro should wait, whether I want a notification, whether the menu-bar icon should indicate the idle state, or whether I want the feature enabled at all.
The application should help me remember, not punish me for having lunch.
The part that actually solves my original problem
Probably the most important feature for me is the Daily Summary.
For any given day, Tomatoro shows all recorded work grouped by task, including the individual record descriptions and totals. Duplicate descriptions are collapsed, and the useful bits can be copied directly.
This is where the whole exercise pays off.
Instead of opening my work log and trying to reconstruct yesterday from IDE history, Git commits, emails, calendar entries, and increasingly unreliable memories, I open the Daily Summary. It tells me which tasks I worked on, for how long, and — thanks to the record descriptions — what I was actually doing.
The task name together with its descriptions can be copied in a form ready to paste into an external work-log entry.
Which is, after all, why I wrote Tomatoro in the first place.
History is editable, because humans are
There is also a detailed Task Records view showing the history of an individual task, grouped by day with daily and running totals.
More importantly, records aren’t sacred just because a timer created them.
Their start time, duration, and description can be corrected afterwards. Descriptions can be added, changed, or removed. Records can be deleted.
I consider this quite important for a tool like this. The goal is to produce a useful account of my work, not to pretend that I always remember to press Start and Stop at precisely the right moment.
Configuration
Finally, Tomatoro has a regular macOS Settings window for the things that shouldn’t be hard-coded just because they happen to suit me.
Among other things, I can configure the default countdown duration, the default duration for manually entered records, menu-bar behaviour, and the idle-reminder threshold and notification options.
None of this is particularly revolutionary.
That’s rather the point.
Tomatoro isn’t supposed to become a project-management platform, an employee-monitoring system, or a comprehensive analytics product. It is a small tool designed to remove friction from one particular activity I dislike doing.
Building a Mac application without being a Swift developer
The other interesting part of this experiment, at least for me, is how Tomatoro was built.
I’m a Java developer. More importantly, after many years of software development, I have fairly strong opinions about how I want software to behave and how I want code to be structured.
What I didn’t have was much practical experience with Swift, SwiftUI, or the details of native macOS application development.
A few years ago, that combination would have made Tomatoro a rather unattractive side project. Before getting to the application I actually wanted, I would first have had to invest a considerable amount of time into learning the language, the UI framework, the application lifecycle, macOS APIs, packaging, and all the little platform-specific details that inevitably appear once a toy application becomes something you use every day.
With a coding agent, the economics changed.
Claude Code could do a large part of the implementation work while I concentrated on specifying what I wanted, trying the result, identifying what was wrong or inconvenient, and deciding what the next iteration should look like.
That doesn’t mean pressing a button and asking an AI to make me an app.
The process was much closer to ordinary iterative software development.
Start with something small. Run it. Use it. Find something annoying. Change it. Discover an edge case. Fix it. Realize that the feature you’ve just added makes another feature desirable. Add that. Refactor when the structure starts getting in the way.
The difference is that the cost of experimenting in an unfamiliar technology became dramatically lower.
Learning by reviewing working code
There is another aspect of this that I find interesting.
Normally, when learning a new language or framework, we tend to start with documentation and tutorials and gradually work towards building something useful.
With Tomatoro, much of that process was reversed.
I already knew what I wanted to build. The agent could propose and implement a Swift solution, and I could learn by reading, questioning, modifying, and reviewing code that solved an actual problem I cared about.
My general software-development experience still transferred surprisingly well. I might not immediately know the Swift syntax or which SwiftUI mechanism should be used for a particular piece of UI, but I can recognize responsibilities, coupling, state management, awkward abstractions, suspicious edge cases, or code that is becoming unnecessarily complicated.
The agent knows things about Swift that I don’t.
I know things about the application that the agent doesn’t.
That turns out to be a rather productive combination.
And whenever something behaves strangely, I have an excellent excuse to learn a little more about macOS application development than I originally intended.
It’s still just software
One thing this project certainly hasn’t convinced me of is that AI-generated code somehow eliminates software engineering.
Quite the opposite.
As Tomatoro grew from a timer into an application with multiple windows, persistent data, notifications, settings, menu-bar integration, background behaviour, and evolving data structures, the usual engineering concerns appeared exactly where you’d expect them.
The current application has separate components for the task and work-record model, persistence, session control, settings, idle detection, notifications, clipboard operations, and the various SwiftUI views. Task data is persisted as JSON, including backward-compatible migration as the format evolves.
In other words, there is no magic boundary where an application stops requiring design because an LLM wrote much of its code.
What changed is how quickly I can explore a solution outside my usual technology stack.
For a personal project like this, that difference is enormous. Tomatoro exists because the distance between “this annoys me; I could probably write a tool for it” and “I’m using the tool” became short enough to be worth crossing.
Source code
Tomatoro is open source. The complete source code is available in the Tomatoro repository on GitHub.
If you’re primarily interested in using or building it, the README contains the current feature list, requirements, build instructions, and an overview of the source tree.
If you’re interested in the implementation, DEVELOPMENT.md goes further into the architecture, development workflow, and some of the macOS-specific behaviour worth knowing before changing things such as the menu-bar item, notifications, or Settings.
The application is written in Swift and SwiftUI, uses the Swift Package Manager, and currently requires macOS 14 or later.
There is still a list of things I may add. There probably always will be.
But Tomatoro has already passed the most important test I have for a personal software project:
I built it because I needed it, and now I actually use it.