We will be undergoing planned maintenance on Oct 7th 6:00AM UTC / Oct 7th 2:00AM ET

Inspiration

Every developer I know has a graveyard of side projects they can barely remember. Not because they stopped caring — but because there was no structure when it was just them and a browser tab.

The real problem isn't tracking progress. It's losing the reasoning. Why did I pick Postgres over SQLite? Why did I abandon the websocket approach halfway through? Why did this project stall in October and never recover? That decision trail disappears the moment you close the tab, and six months later you're starting the same project again making the same mistakes.

I'd tried Notion docs, WIP.co, even plain text files. Notion became a mess. WIP solves motivation through public accountability — streaks, feeds, building in public — which is a different problem entirely. What I wanted was the opposite: private, structured, and focused on capturing why I built things, not that I built them.

An Ask HN thread on developer diaries crystallised it. The answers revealed what developers actually want: somewhere to capture decisions, dead ends, and "why I chose X over Y" that they'd otherwise lose forever. That's the emotional core HackLog is built around.


What it does

HackLog is a personal project tracker for developers, built around one insight: the thing developers keep losing isn't progress, it's the reasoning.

For each side project you log:

  • Status — Idea, Building, Shipped, or Abandoned. Honest accounting, not just the wins.
  • Tech stack — what you actually used, as searchable tags.
  • Key decisions — a timestamped, append-only log. Each entry captures the decision made, alternatives you considered, and why you chose this path. Immutable after saving — edit breaks the audit trail, delete acknowledges the entry shouldn't exist. Those are different things.
  • Lessons learned — retrospective reflection after a project ends or stalls. Separate from decisions deliberately: decisions are in-the-moment, lessons are after-the-fact. They serve different moments.
  • Links — up to 3 per project: repo, live URL, Notion doc, whatever matters.

The dashboard gives you a filtered overview by status. Clicking into a project shows the full record. The decision log is inline on the detail page — no navigation, no lost train of thought. You see something worth capturing, you log it in 30 seconds, you're back in context.

All data lives in localStorage. No backend, no login, no sync. It opens instantly and works offline. The data is yours and it stays on your machine.


How we built it

Built using spec-driven development — the process taught by this hackathon. Planning artifacts before a single line of code.

The stack: React 18, Vite 8, Tailwind CSS v4, React Router v7, localStorage. No TypeScript (solo 4-hour build, type safety doesn't pay for itself here). No component library — the Linear/Raycast aesthetic required full CSS control. No date library — native Intl.DateTimeFormat is sufficient for displaying ISO strings.

The architecture in one paragraph: A useProjects custom hook owns all localStorage access — nothing else touches it, no exceptions. ProjectsContext delivers state and mutations to pages. Four routes, three page components — create and edit share a single ProjectForm with a mode prop rather than two files that drift apart. The decision log inline form is local state on ProjectDetailPage, not a route — leaving the page to log a decision breaks the flow completely.

The data model decision that shaped everything: decisions is an array of structured objects, not a free-text field. Each entry has decision (one line), alternatives (optional), reasoning (required), and createdAt (immutable). No updatedAt on decision entries — because nothing ever updates. That absence is intentional.

Build order: Storage and utils first, then the state layer, then the app skeleton, then pages bottom-up — Dashboard (read-only, simplest) before ProjectDetail before ProjectForm (most complex, built last when the foundation is solid). Components built alongside the page that first needs them, not before.

The spec-driven process caught three things that would have been discovered during build instead of upfront: the decision log field was missing entirely from the first PRD draft, the endDate clearing behaviour on status change needed an explicit decision, and the post-creation navigation (detail page, not dashboard) needed to be committed to before the form was written.


Challenges we ran into

Tailwind v4 is a significant departure from v3. The tailwind.config.js file is gone entirely. Configuration now lives in CSS via an @theme directive. Coming from v3 projects, the setup process was unfamiliar — no config file to reach for, different dark mode behaviour, different plugin integration for Vite. Worth flagging for anyone else on v3: read the v4 migration docs before scaffolding, not after.

The decision log UX required more thought than expected. The core tension: it had to feel fast enough to use in the middle of active building, but structured enough to be scannable two years later. Inline collapsible form (not a route) was the right answer, but getting the collapse/expand behaviour and the read-only state to feel right took iteration. The immutability constraint — no edit, only delete — also required explicit UI decisions about affordances so it didn't feel broken.

Scope discipline under time pressure. The temptation to add search, keyboard shortcuts, and a timeline view was real. Every one of those is a good idea. Every one of them went on the out-of-scope list anyway. The scope document made that easier — when the urge to add something hit, the question became "is this in the spec?" not "is this a good idea?" Those are different questions.

The endDate edge case. Changing status from Shipped back to Building should clear the endDate field — no hidden state persisting in the background. Deciding this upfront in the spec (rather than discovering the edge case during build) saved a confused bug report. The principle that emerged: if it's not visible, it doesn't exist.


Accomplishments that we're proud of

The decision log is genuinely useful. I used HackLog to document the decisions I made while building HackLog. The first entries — why localStorage over a backend, why append-only on decision entries, why URL routing over modals — are sitting in the app right now. That recursive loop is the best proof the core value proposition works.

The spec caught things code wouldn't have. Three features that would have been missing, broken, or wrong if we'd skipped straight to building: the decision log itself (absent from the first PRD pass), the endDate clearing behaviour, and the post-submit navigation. Spec-driven development working exactly as advertised.

The data model is clean enough to be proud of. No null checks scattered through the render layer — optional fields store empty strings, not null. updatedAt on the project refreshes on every write including decision log mutations. Decision entries have createdAt and nothing else — the absence of updatedAt is a design decision, not an oversight.

Shipped in 4 hours. The scope document's out-of-scope list made this possible. Explicit cuts — no search, no sharing, no dark mode toggle, no reminders — meant 4 hours went to the 9 user stories that matter, not 9 half-finished features.


What we learned

"The thing developers lose isn't progress, it's the reasoning." That line came out of the scope conversation and it's the clearest articulation of what HackLog is for. Writing the scope document forced the insight. Without it, the app would have been a generic project tracker with a slightly different UI.

Scope discipline is a design tool, not just a project management tool. Writing the out-of-scope list explicitly — and giving reasons for each cut — changed what got built. Dark mode moved from out-of-scope to in-scope when I realised my stated aesthetic benchmarks (Linear, Raycast, VS Code) made light mode a tool I'd never actually use. The scope document caught the contradiction.

Decisions and lessons learned are different things. Decisions are in-the-moment — why I chose X while I was building. Lessons are retrospective — what I'd do differently after the project ends. Bundling them into one "notes" field would have lost that distinction. Separate fields, separate moments, separate value.

Append-only isn't just a technical constraint — it's a design value. The immutability on decision entries exists because edit breaks the audit trail. You can silently revise history. Delete acknowledges the entry shouldn't exist. That distinction — which feels philosophical — produces concrete UI decisions: no edit button, delete with confirmation, createdAt and nothing else on the Decision object.

Spec-driven development compounds. Each planning conversation built on the last. The data model fell out of the PRD. The component tree fell out of the data model. The build order fell out of the component tree. By the time /build ran, the agent had no ambiguous decisions to resolve — every architectural choice was already made.


What's next for HackLog

The scope document already has a v2 list. In priority order:

Search — full-text across project names, descriptions, stack tags, decision entries, and lessons. The decision log makes search genuinely valuable: finding every project where you considered GraphQL, or every project you abandoned for the same reason.

Export — a structured JSON export of everything, and a readable Markdown export per project. The decision log is valuable data; it shouldn't be trapped in localStorage forever.

Cross-device sync — localStorage is the right call for v1, but the data should eventually follow you. A lightweight backend (probably a single Cloudflare Worker with KV storage) with no auth complexity — just a passphrase-protected sync key.

Timeline view — a chronological view across all projects showing when decisions were made, when statuses changed, when projects stalled. The data model already supports this; it just needs a rendering layer.

Keyboard shortcuts — N for new project, D on detail page to log a decision, E to edit. Linear-native muscle memory for a tool built to feel like Linear.

The foundation is solid enough that none of these require architectural changes. That was the point of getting the spec right before building.

Built With

Share this project:

Updates

Submission history