I'm 19. I've been building things that never shipped for two years.
Four months ago I decided to build one thing and refuse to abandon it. Not a clever thing. A boring thing that runs every day whether I show up or not.
Today it published its 100th article. I haven't touched it in most of the last two weeks. It costs me nothing but a $5 VPS.
Here's everything that broke on the way, because that's the part nobody posts.
The stack
One cron job runs generate_article.py:
- Picks a non-duplicate topic
- Generates a 1,200 to 2,300 word technical article
- Creates a cover image
- Writes a Hugo markdown file and rebuilds the site
- Cross-posts to Dev.to with a canonical tag
- Pings IndexNow
- Sends me a Telegram summary
Hugo serves static files behind nginx. That's the whole system. No framework, no queue, no orchestrator. Just urllib and a cron line.
Failure 1: the topic generator ate itself
I seeded 30 topics. After those ran out, it generated its own.
Within weeks it was producing "X vs Y vs Z" permutations of the same four tools. Seven near-identical "n8n vs Make vs Zapier" articles, each with confidently invented pricing tables that contradicted each other. My dedup check started rejecting the new ones, the queue flatlined, and publishing stopped for four days before I noticed.
The fix was deterministic category rotation. The generator now cycles a fixed list of subject areas and is explicitly forbidden from writing comparison articles. Plus a stopword list built from the most overused tokens across everything already published.
Lesson: an LLM told "generate something new, don't duplicate these" will find the narrowest possible way to comply.
Failure 2: every article was truncated and I couldn't see it
The generation call inherited a 3,000-token default. A 1,400-word article with code blocks blows straight past that. Articles ended mid-code-block, mid-sentence, sometimes mid-heading.
I didn't notice for two months, because of two things I'd added to help:
- An auto-close for unbalanced code fences, which made truncated articles look complete
- A
MIN_WORD_COUNT=800check that truncated articles still passed
Two safety nets, both hiding the exact thing they should have caught.
Lesson: a defensive patch that silently repairs bad output will hide the bug producing it. Log every time it fires.
Failure 3: the retry loop retried nothing
When an article came back short, the code retried with the identical prompt. Five identical attempts, five similar results, then it shipped the last one with a warning.
I added one line to the retry: the previous attempt's word count, plus "you must exceed 1,200 words, expand architecture and config examples, do not pad."
Same model. Same temperature.
624 words, then 1,076 on the retry.
Lesson: if your retry doesn't tell the model why it failed, it isn't a retry. It's a coin flip you're paying for five times.
Failure 4: free tiers move under you
The engine runs a three-provider chain: Gemini, then Groq, then Anthropic. Any provider fails, the next catches it. Building that surfaced things no documentation mentions:
- A model ID that's listed by the API can still 404 with "no longer available to new users." Enumerate models against your own key. Never trust the docs page.
- Gemini 3.x Flash reasons mandatorily.
thinkingBudget: 0returns a 400, and thinking tokens bill againstmaxOutputTokens. Give it a small budget and thinking eats the entire thing, so you get empty text andfinishReason: MAX_TOKENS. - Gemini quota is per-project, not per-key. Extra keys in the same project add zero headroom.
- Groq's free tier caps at 6,000 tokens/minute, so a single 8,000-token request 429s instantly then succeeds on backoff. In logs that looks like a dead provider. It isn't.
Failure 5: the image model wrote words I never asked for
Covers come from Cloudflare Workers AI (FLUX.1 schnell), free. My prompt said "no text, no words."
FLUX rendered headlines anyway. Garbled ones.
"Configuring Swaꝺ Space on Cheapy Linux" went out as the OG image on a live post and sat there for days.
The fix wasn't a stronger negative prompt. It was deleting the article title from the image prompt entirely.
Lesson: feeding a headline to an image model is asking it to render a headline, no matter what you append afterward.
Failure 6: it published its failures instead of reporting them
This one's the most recent, and the one I'm proudest of fixing.
When every provider hit quota, generation fell through to a weaker model, came back at 609 words, logged a warning, and published anyway. Thin pages went live on three surfaces and the topic was burned forever.
Now the length gate raises before any write happens. Nothing reaches Hugo, Dev.to, or the state file. The topic stays queued. I get a Telegram alert with the word count and which provider served it.
Four days later it fired for real. I got the alert, found a silent JSON truncation in the topic call, fixed it, and article #100 published that evening.
Lesson: a pipeline that fails loudly beats one that degrades quietly. Every time. Build the alarm before you need it.
Where it is now
- 100 articles live, 1,200 to 2,300 words each
- Textless 1200x630 covers, generated free
- Two platforms, proper canonical tags
- $0/month in API costs
- 14 days unattended, zero intervention
What it does not do
Rank instantly. SEO takes months and my domain is still climbing out of the sandbox. Revenue so far is zero.
Anyone showing you traffic screenshots from week two of an "AI content engine" is selling you something that isn't the engine.
The part I actually want to say
I nearly killed this thing twice.
Once in May, when the topic generator broke and I let a diagnosed fix sit unapplied for seven weeks because I was busy and it felt like too much.
Once in July, when I found out around 30 of my articles were near-duplicate doorway pages and the honest move was deleting a third of everything I'd built.
I did the deletion. 76 articles down to 51. It felt awful. It was the single best decision in the project.
If you've got a half-built thing sitting in a folder right now, the problem probably isn't that it's too hard. It's that fixing it means admitting part of it was wrong.
Go delete that part. The rest gets better immediately.
Ship the boring version. Let it run. Fix it when it screams.
I packaged the whole thing, engine, Hugo theme, nginx config, and every free-tier gotcha above written into the README, as System 3 for anyone who'd rather skip the four months.
But honestly? The six lessons above are the actual product. Take them and build your own.
What's the thing you've been avoiding fixing? Drop it in the comments and I'll tell you how I'd approach it.
Top comments (1)
Happy to go deeper on any of these. The one I'd most want feedback on is failure 6 - the length gate. Curious whether other people building unattended pipelines default to "publish something degraded" or "publish nothing and alert." I flipped from the first to the second and it's been the single biggest reliability improvement.