The indie hacker's build log answers two questions
Most writing about build logs treats them as audience-facing: post updates, build in public, get attention. That use is legitimate and covered elsewhere on this blog.
The private version has a narrower job. A build log for your own use exists to answer two questions that come up repeatedly on any project lasting longer than a few months. Both answers decay quickly, and neither one is recoverable from your repository.
Question one: how long did that actually take
Every estimate you make is based on remembered duration, and remembered duration is unreliable in a consistent direction.
Ask yourself how long the payments integration took. The answer that comes back is shaped by the part you remember, which is the part that was interesting or painful. The three days of reading documentation before writing any code are not in there. Neither is the week where you touched it twice because something else was on fire.
So you say “about a week,” and the real answer, if anyone had been counting, is closer to a month of calendar time containing maybe six working days of attention.
This matters because it is the input to every future estimate you make, including the ones you give other people. Consistently underestimating by a factor of three or four is not a discipline problem. It is what happens when the estimate is derived from a compressed memory of the last similar thing.
A build log fixes this by not being a memory. If the record shows the first commit touching that subsystem on the 4th and the last on the 29th, with gaps you can see, then you have the calendar duration and the density, and both are facts rather than impressions.
Question two: why did I decide that
This is the more painful one.
You return to a project after two months away. There is a decision embedded in the code that is clearly deliberate, has no comment attached, and makes no sense to you now. A weird retry policy. A denormalised column. A dependency you would not have chosen.
Git tells you what changed and when. It does not tell you that the retry policy exists because the upstream API returned 502s for a whole afternoon in March and you got tired of it. That fact lived in your head for a week and then left.
The cost of losing it is not abstract. Either you leave code you do not understand in place because you assume past-you had a reason, or you remove it and rediscover the reason the hard way. Both are expensive. Both are common.
Neither answer is in your repository
Worth being precise about why the obvious sources fail.
Commits record changes, not effort or reasoning. A one-line commit can represent a day of work, and a 400-line commit can be a mechanical rename.
Issue trackers record intent at the time of writing, which is usually before you learned anything. The ticket says what you planned to do, and the interesting part is always what you found out instead.
A time tracker gives you duration without content. Four hours on “backend” in March is a number attached to nothing.
Your public updates, if you post them, are curated. Nobody posts the Tuesday where they broke staging and reverted twice, which is the Tuesday with the most information in it.
Each source holds a slice, all of them are dated, and none of them is joined to the others. Reconstructing one week means opening five tabs and lining up timestamps, which is exactly the work nobody does.
What an assembled record gives you
deariary joins them. It reads your connected services for a day, currently thirteen of them (GitHub, Linear, Todoist, Toggl Track, Slack, Discord, Bluesky and others), and writes one entry per day out of what they logged.
For the first question that is immediately useful. A month of entries gives you calendar duration, the gaps, and what else was competing for attention during the stretch you remember as continuous work. It is a better estimation input than your memory, because it was not produced by your memory.
For the second question it gets you most of the way but not all of it. The entry records that you were in that file that afternoon, that the deploy went out twice, that there was a conversation in a channel at the same time. That narrows down where an answer lives. It does not contain the reasoning, because the reasoning was never typed anywhere.
Making the “why” survive
The gap above is closeable, and cheaply, but only by you.
The lowest-effort version is to put the reason in the commit message when there is one. Not the change, which the diff already shows. The reason: “retry on 502, upstream was flaky all afternoon.” One clause. Those messages flow into the entry along with everything else, and a year later they are the difference between a mystery and a decision.
If you would rather keep it out of the repository, a single line pushed through the webhook does the same job. Either way the principle is the same: capture the reason at the one moment you know it for free, because that window closes within about a week.
What this does not do
It does not tell you whether the project was going well. That judgment is not in any of the sources, and the entry will not offer one.
It does not capture thinking that never touched a tool. An afternoon walk where you worked out the architecture leaves no trace anywhere.
And it does not reach backwards. Connecting today covers the last seven days and continues forward, so the first six months of a project already built are not recoverable this way.
The honest reason to bother
A solo project’s history exists in exactly one place, which is your head, and it degrades faster than anyone expects. Two months is enough to lose why. A year is enough to lose how long.
Neither loss announces itself. You notice the first when you are staring at your own code with no idea what you were thinking, and the second when you promise someone a week and deliver in five.