I promised in the v1 retrospective that v2 would get a real roadmap before any code gets written. Here it is, the actual plan, published so future me cannot pretend it said something else. This post is only about what is coming. What already shipped gets its own posts on this devblog, that is what the devblog is for.
The core idea is ordering by dependency instead of enthusiasm. Almost everything I want hangs off a few load-bearing pieces, so those go first even though they are the least fun to build:
+----------------------+
| email service | the keystone
+----------+-----------+
|
+------------+----------+-----------+-------------+
v v v v
password reset contact alerts reply notifications newsletter
+---------------------+ +---------------------+
| central log service | | backups (DB+media) |
+---------------------+ +---------------------+Wave one is infrastructure. The email service comes first because four separate features I want all need to send mail, so mail becomes its own tiny service with a durable outbox, and everything else just asks it to send things. The hard part of email is not SMTP, that is a library call. The hard part is everything around it: not losing a mail when the process restarts, not sending the same mail twice when a retry races a success, and deliverability, the dark art of SPF, DKIM and DMARC records that decides whether my mail lands in an inbox or a spam folder. I expect to spend more time reading DNS docs than writing Go.
Backups are in wave one because they have no dependencies and no excuse. The tricky part of backups is not taking them, cron can do that. It is proving they restore. An untested backup is a feeling, not a backup, so the plan includes actually restoring to a scratch database on a schedule and checking the result. Off-box copies too, because a backup on the same disk as the thing it backs up protects against exactly one failure mode, and not the scary one.
The central logging service is the honest oddball. A personal blog does not need one, the per-service error tables already work. I am building it anyway because the patterns inside it are the whole point: an outbox shipper that batches rows to a central sink, retries when the network drops, and a consumer that stays correct when the same batch arrives twice. Those are patterns worth learning on a system where the blast radius is my own blog. I am writing that down so I do not accidentally gold-plate it into something enterprise-shaped.
Wave two is everything that needs mail. Password reset sounds trivial and is full of sharp edges: reset tokens that expire and can only be used once, and answers that do not reveal whether an email address has an account here. Email verification rides along. Contact form alerts are the easy warm-up. Comment reply notifications need a way to opt out per thread, nobody wants mail forever because they once commented. The newsletter is the deep end: subscriber consent, unsubscribe links that always work (that part is law, not politeness), and sending slowly enough that my one small server does not get itself blacklisted.
Wave three is the fun one. A bulk photo uploader and gallery, because this blog is slowly becoming a photo collection of hikes with a blog attached. Tough parts: getting hundreds of files through a browser upload without one failure ruining the batch, generating thumbnails server-side so the gallery does not ship full-size photos to your phone, and stripping EXIF data before publishing, because a photo that quietly carries GPS coordinates is a privacy leak wearing a nice sunset. Post series come here too, linking posts into an ordered collection, which is more of a modeling question than a coding one.
Wave four is reach. ActivityPub is the boss fight: making posts federate so people can follow this blog from Mastodon. It means actors, inboxes, outboxes and HTTP signatures, and the folklore says every server speaks a slightly different dialect of the spec, so most of the work is debugging federation with servers I do not control. Webmentions are the lighter warm-up version of the same idea. A translation helper for the three languages this blog runs in belongs here as well: translate once at write time and store it (a page view must never cost an API call), detect when the source changed so a translation can be marked stale, and always land machine output as a draft for me to edit, never straight to publish. And offline reading as a PWA, where the famous trap is cache invalidation serving readers a stale site forever.
Each of these ships as its own small finished change, not one giant v2 branch that lives for a month. If only wave one lands well, v2 was already worth it. The boring stuff first, on purpose, in writing. Hold me to it.