I built SnippetLab because I kept losing my own code.
Not in a dramatic way — just the ordinary thing where you solve a small problem, write twelve lines that work, and then three weeks later you need those twelve lines and they are gone. In a Slack message. In a file called test2.js. In a terminal you closed.
A shelf for code snippets. You paste something, tag it, and it is there later. That is the whole idea, and it took a while to get the shape right, because "a shelf for snippets" sounds trivial until you build one and realise a list of code blocks is useless — you cannot find anything, and nobody trusts a snippet they cannot read first.
So the detail page is the product. It shows the code with real syntax highlighting and a line gutter, a breadcrumb saying where the file is meant to live, and five things you can do with it: copy, download, view raw, embed, remix.
The part that was harder than expected
Real-time chat.
Not the chat itself — a message list is a weekend. The hard part was everything around it. Voice notes meant MediaRecorder, which means a MediaStream you have to release in *every* exit path, because a stream left open keeps the microphone indicator lit and on some phones keeps the mic genuinely hot. Replies meant a self-referencing foreign key and a decision about what happens to a reply when the thing it quotes is deleted. Reactions meant a unique constraint on (message, sender, emoji), because without it a double-tap on a slow connection inserts two rows and the count reads 2 for one person.
And all of it had to work with row-level security on, which means the database — not the browser — decides who can read and write what.
Three bugs worth naming
Soft delete versus a check constraint. I had a constraint requiring a non-empty body on any text message. Deleting a message blanks the body but keeps the row, so every single delete failed the constraint. The delete button did nothing, quietly, for as long as it existed.
A click loop. Clicking someone's avatar in chat closed the modal and then re-dispatched the click so the router could open their profile. The re-clicked element was still inside the closed modal, so it matched the same handler again, forever. The profile page sat on "Loading" while it reloaded itself faster than it could render.
View counts that never counted. The views column existed. The seeded demo rows carried plausible numbers. Nothing ever incremented it, so every real snippet sat at 0 and the "Most viewed" sort was sorting noise.
None of those were caught by reading the code. They were caught by opening the thing and using it.
What I would tell someone building the same
Write the test that uses the feature the way a person does. Not a unit test of the render function — an actual call that signs in, sends a message, reacts to it, and reads it back. Mine is 13 checks against the live database and it found the delete bug on the first run.
> The code you do not run is the code you do not know works.
SnippetLab is live, the source is on GitHub, and it is still the project I would point at if you asked what I actually do all day.