This is the last post in the Building Postlark series, and honestly, the hardest one to write. Thirteen posts about features and design decisions — those are comfortable. Talking about what it actually felt like to build the whole thing alone? Less so.
The plan said six weeks
When I first scoped Postlark, the timeline was six weeks. I had a spreadsheet. Phases, milestones, buffer days for unexpected problems. Week 1: API and data layer. Week 2: blog rendering and dashboard. Week 3: MCP server, CLI, SEO tooling. Weeks 4 through 6: billing, admin, polish, launch. Clean and methodical.
That spreadsheet became fiction within 48 hours.
What actually happened
The first commit landed on March 22nd. Monorepo scaffolding, dependency setup, planning docs finalized. Two days later, something clicked and the pace went vertical. The commit log from March 24th alone covers what was supposed to take the entire first two weeks: API foundations, CRUD for posts and blogs, key management, security hardening, the blog rendering engine, default themes, RSS feeds, sitemaps, the dashboard scaffold, the markdown editor, post management UI, blog settings — fourteen "build days" worth of work collapsed into a single calendar day.
I didn't plan for that. I just... didn't stop.
By March 27th — five days after that first commit — the platform had an MCP server published on npm, a CLI tool on its fourth version bump (more on that disaster shortly), a fully rewritten landing page, image upload support, and a mobile reader app scaffolded with three phases of features. Sixty-three commits that day. I'm not proud of that number. It means I didn't eat properly.
The naming detour nobody asked for
Before any code existed, I spent an embarrassing amount of time on the name. The first candidate was AirPress. I hated it immediately — sounded like a knockoff AirPods case. Then came a parade of alternatives: Penlark, Plume, Inkless, Castmill, Postwise. I actually secured penlark.ai before discovering someone had already claimed the GitHub username.
Back to square one. Twenty more candidates. Domain checks, npm registry checks, GitHub username checks, X handle checks. Postlark survived the gauntlet — .ai domain available, GitHub org unclaimed, npm namespace clear, even the X handle was free. That last part felt like finding a parking spot in Gangnam on a Friday night.
The .ai TLD wasn't vanity. For a platform whose entire identity is AI-native publishing, the domain needs to signal that before anyone reads a single word of copy.
The CLI versioning fiasco
Here's something the previous posts in this series politely omitted: the CLI launch was a mess. Version 0.1.0 had a bin wrapper that didn't survive npm's publishing process. Patched it, bumped to 0.1.1. Then the workspace dependency resolution broke — pnpm workspaces use workspace:* internally, which npm doesn't understand. Bumped to 0.1.3. Then the browser-based login flow needed work for headless environments. 0.1.4. Then I realized the version display was hardcoded instead of reading from package.json. 0.1.5.
Five versions in a single day, each fixing something that should have been caught the version before. The npm registry remembers everything. Those early versions are still there, quietly judging me.
The Reader app rabbit hole
The Reader app wasn't in the original plan at all. It started as "maybe a simple mobile view would be nice" and within hours I was knee-deep in Capacitor, Android build tooling, and WebView edge cases that I wouldn't wish on anyone.
The blank screen bug was the worst. The app would install fine, show the splash screen, then... nothing. White screen. Traced it to a service worker that worked perfectly in the browser but caused navigation failures inside Android's WebView. I ripped out the entire PWA setup and replaced it with a simpler loading strategy. That fix alone took three separate attempts across two days, and the commit messages tell the story: "fix: blank screen on first install", then "fix: unregister SW in index.html before modules load", then finally "fix: remove VitePWA — service worker caused blank screens on navigation."
Sometimes the right solution is just deleting the thing that's causing problems.
Then came the half-screen bug — the WebView would render at half height after the app resumed from background. And splash screen sizing issues. And Google Sign-In integration on Android using Credential Manager. Each one a multi-hour detour from the "real" work.
What I shipped
The final tally, ten days after that first commit:
A REST API with full blog and post management
A markdown editor with live preview, GFM, Mermaid diagrams, KaTeX
A blog rendering engine with custom themes, dark mode, and CSS injection
An MCP server on npm — the thing that makes the whole "AI-native" pitch real
A CLI tool (eventually stable, after version 0.1.6)
Auto-generated OG images with Korean font support
Scheduled publishing
Comments via giscus
Custom domain support
Server-side analytics with zero client JavaScript
A landing page in English and Korean
A documentation site
An admin panel
A mobile reader app on Android
An Explore page for discovering blogs on the platform
That's not a humble brag. It's context for the next section.
The honest part
Shipping all of that in ten days meant cutting corners I'll be paying for later. Test coverage is thin in places. Some error messages are developer-hostile. The admin panel was built for exactly one user (me) and it shows. Parts of the dashboard could use another design pass. The documentation has gaps.
There's a specific kind of technical debt that comes from building fast: it's not spaghetti code or bad architecture — the foundations are solid. It's more like a house where the structure is sound but half the light switches are in weird places because you installed them at 2 AM and "close enough" felt like "done."
I also burned out. Not the dramatic kind — just the quiet kind where you wake up one morning and the thought of opening your editor feels heavy. That passed after a few days, but it's worth mentioning because solo dev narratives tend to skip that part. The ten-day sprint wasn't sustainable and I wouldn't repeat it.
So was the six-week plan wrong?
No. The plan was reasonable for a normal development pace. What changed was that once I started, the feedback loop of "build, see it work, build more" created a momentum that the spreadsheet couldn't have predicted. The six-week buffer also assumed I'd hit more blockers. Some showed up — the CLI publishing issues, the WebView bugs — but most of the feared unknowns just... didn't materialize.
If you're a solo developer thinking about building something ambitious: plan conservatively, but don't let the plan cap your pace. The spreadsheet is a safety net, not a speed limit.
This wraps up the Building Postlark series. Thirteen posts about what we built and why, and one about what it cost. If you've followed along from post one, thanks for reading. The platform is live, the npm packages are published, and the next chapter is about what happens when real users show up and break everything I thought was finished.