themissingsunday
#project-spec#architecture#documentation#ai#tutorial#automation

How to Write a Project Spec That Actually Works

A walkthrough of the project specification behind feedmeup — the vision, the diagrams, the pipeline — and why writing one before you code saves you from building the wrong thing.

Most side projects start the same way. You get an idea, open a terminal, start coding, and three weeks later you have 2,000 lines of something that sort of works but you cannot explain to anyone — including yourself. I did this for years. Then I wrote a project spec before writing a single line of code, and it changed how I build everything.


This is the story of the spec I wrote for feedmeup — a news aggregation platform that pulls from 140+ sources, filters by keyword, deduplicates with fuzzy matching, synthesizes with AI, and publishes to a static site on a schedule. The spec is around 1,000 lines. The engine is around 3,000. Writing the spec first meant the engine was right on the first pass instead of the fifth.

If you are building something nontrivial — a side project, a tool for your team, an automation pipeline — a project spec is the single highest-leverage document you can write. This post walks through what mine looks like, why each section exists, and how you can write one for your own project.

#Why Bother With a Spec?

Three reasons.

It forces clarity. When you write “fetch 140+ feeds in parallel with retry logic,” you have to decide: how many retries? What backoff strategy? What HTTP status codes trigger a retry? These decisions made in a spec are deliberate. Made in code at 11pm, they are whatever autocomplete suggests.

It gives AI context. If you are using GitHub Copilot or any AI coding tool, a spec is the single best prompt you will ever write. Drop it in your repo as a reference document, and every conversation with your AI assistant starts from a shared understanding of what you are building. No more explaining the architecture from scratch every session.

It prevents scope creep. When the spec says “Phase 1: stable weekly aggregation,” you stop yourself from building a real-time alerting system on day two. The roadmap is written down. You can see where you are and what comes next.

#Section 1: Vision and Goals

Every spec starts with what you are building and why. Not a feature list — a statement of purpose.

Mine opens with:

feedmeup is an automated, cloud-hosted news aggregation and curation platform that transforms raw RSS feeds into professionally formatted, AI-synthesized weekly blog posts published on GitHub Pages.

One sentence. Anyone reading it knows exactly what this thing does. Then I break it into primary goals (the MVP) and secondary goals (future phases):

Primary goals — the things that must work before anything else matters:

  • Automate weekly curation from 140+ trusted sources across 6 categories
  • Apply intelligent filtering using keyword matching and fuzzy deduplication
  • Synthesize insights using AI to identify trends and cross-source patterns
  • Publish to a static site with zero manual intervention
  • Enable customization through YAML configuration — no code changes needed

Secondary goals — explicitly labeled as Phase 2 and Phase 3, so you know they are not blocking launch:

  • Enhanced analyst opinions with multi-layered AI analysis
  • Historical context lookup comparing new incidents to past coverage
  • Investigative journalism depth with technical deep-dives

The separation matters. Without it, everything feels equally urgent, and you end up half-building five features instead of fully building two.

#Section 2: Architecture Overview

This is where most specs either go too deep or stay too shallow. You do not need UML diagrams generated by enterprise tools. You need a picture that shows how data flows through your system.

Here is the architecture diagram from my spec, translated into something interactive:

Generating Diagram

This diagram tells you three things at a glance:

  1. Data flows one direction — from feeds to processing to output to deployment to live site.
  2. There are two outputs — a weekly scan (curated links) and an analyst opinion (AI-generated analysis).
  3. Deployment is split — a generate step and a push step, running in separate workflows for safety.

You do not need to understand every function to get the architecture. That is the point. A good architecture diagram is a map, not a manual.

#Section 3: The Tech Stack Table

I keep a simple table mapping each layer to a technology and a one-line reason for choosing it:

LayerTechnologyWhy
ProcessingPython 3.12Core pipeline — fetch, filter, deduplicate, format
Feed ParsingfeedparserHandles RSS 2.0, Atom 1.0, malformed XML gracefully
Fuzzy MatchingRapidFuzzDeduplication across 140+ sources at 80% threshold
HTML CleanupBeautifulSoup4Extract plaintext from messy feed HTML
AI SynthesisGoogle GeminiTrend analysis and opinion generation
Site GeneratorAstroStatic-first with component islands for interactivity
ComponentsSvelteInteractive search, theme toggle, diagrams
HostingGitHub PagesFree, integrated with repository
AutomationGitHub ActionsScheduled + event-driven workflows
CIpytestSmoke tests on PR and push

Why a table and not paragraphs? Because six months from now when you need to swap out a dependency, you want to find the answer in three seconds, not three paragraphs.

#Section 4: Data Flow — Step by Step

This is the longest section in the spec, and the most valuable. It walks through exactly what happens when the pipeline runs. Here is the flow:

Generating Diagram

Each box in this diagram maps to a function in the codebase. When I built the engine, I implemented one box at a time — load config, then fetch feeds, then filter, then deduplicate. The spec gave me the order. The code followed naturally.

A few decisions that the spec forced me to make upfront:

  • Lookback window: How far back should we look for articles? I settled on 30 days for most categories, 28 days for regulatory content (regulations move slower but stay relevant longer).
  • Keyword matching: Literal strings matched at word boundaries. The word “ai” matches “ai” but not “paid.” This seems obvious, but without writing it down I would have used substring matching and gotten garbage results.
  • Domain diversity: Maximum 2 articles per domain in highlights. Without this rule, one prolific source dominates the entire output.
  • Deduplication threshold: 0.8 (80% similarity). Too high and you miss near-duplicates. Too low and you merge unrelated stories. This number came from testing, but the spec is where I documented the decision.

#Section 5: Configuration as a Feature

One of the best decisions I made was treating configuration as a first-class feature in the spec. The spec explicitly defines what is configurable and what is hardcoded:

# What the spec defines as configurable:
performance:
  fuzz_threshold: 0.8        # Fuzzy match sensitivity
  max_per_domain: 2           # Domain diversity limit
  max_results: 10             # Top N highlights
  request_retries: 3          # HTTP retry count
  timeout_seconds: 15         # Request timeout
  lookback_days: 30           # Default article window

And it defines fallback behavior — if any value is missing from the config file, the code falls back to documented defaults. No crashes, no undefined behavior.

This matters because six months from now, when you want to tweak the dedup threshold or add a new source, you do not touch code. You edit a YAML file. The spec made that decision before any code existed.

#Section 6: Error Handling — The Boring Essential

Most specs skip error handling. Mine has a full section on it, and it has saved me more than any other part.

ErrorWhat HappensWhy It Matters
HTTP 403/404Save error to _errors/ directory, continueOne bad feed does not crash the entire run
Parse failureLog details, save error post with exceptionYou can debug after the fact
Timeout (15s)Retry 3x with exponential backoffTransient failures resolve themselves
Rate limit (429)Retry with backoffRespects server limits
No entries in feedLog warning, continueEmpty feeds are normal
Malformed XMLfeedparser handles gracefullyNever crashes on bad input
Missing article URLSkip href, still display titlePartial data is better than no data

Writing this table forced me to think about every failure mode before I encountered it in production. When the first 403 error hit a feed, the code already knew what to do — because the spec already said what should happen.

#Section 7: The Phased Roadmap

The spec breaks the project into three phases with clear boundaries:

Phase 1 — Stable weekly aggregation. Fetch feeds, filter, deduplicate, format, publish. No AI. Just reliable, automated curation.

Phase 2 — AI-enhanced analyst opinions. Article-of-the-week format. Historical context lookup. Risk assessment. This is where Gemini enters the pipeline.

Phase 3 — Investigative journalism depth. Technical deep-dives with threat intelligence, attribution analysis, and actionable defense strategies. The spec describes the exact post structure — section by section, paragraph by paragraph, word count targets per section.

Why this level of detail? Because when you tell an AI model “write an analyst opinion,” you get generic output. When you tell it “write a 400-600 word technical deep-dive covering attack chains, defense failure analysis, historical comparison, and real-world impact in an investigative journalism tone,” you get something worth publishing.

The spec is the prompt. The more specific the spec, the better the output — from both AI and human contributors.

#Section 8: Deployment and Validation

The spec includes a deployment checklist. Not as an afterthought — as a required section:

Pre-deployment:

  • All tests pass
  • Config YAML syntax valid
  • At least one feed accessible
  • Posts generated in output directory
  • Front matter validates against schema
  • No secrets hardcoded

Post-deployment:

  • Workflow completes green
  • New posts visible on live site
  • Navigation works
  • Content formatted correctly
  • Mobile responsive

This checklist exists because I learned the hard way that a green CI checkmark does not mean your site works. I have had workflows succeed while deleting every post on the live site. The previous post covers that story in detail.

#How to Write Your Own

If you are starting a project — whether it is a feed aggregator, a CLI tool, an API, or anything with more than one moving part — here is the structure I recommend:

  1. Vision (1 paragraph) — What does this thing do? Who is it for?
  2. Architecture (1 diagram) — How does data flow through the system?
  3. Tech stack (1 table) — What technologies, and why each one?
  4. Pipeline (step by step) — What happens when your system runs? Every step, in order.
  5. Configuration (with defaults) — What is tunable? What are the safe defaults?
  6. Error handling (every failure mode) — What breaks, and what happens when it does?
  7. Phases (with boundaries) — What is MVP? What comes later?
  8. Deployment checklist — What must be true before and after you ship?

You do not need 1,000 lines. You can write a useful spec in 200. The act of writing it — of forcing yourself to answer these questions before you code — is what matters.

#The Meta-Lesson

The most surprising thing about writing a spec first was how much faster I built the actual system. Not slower — faster. Because every function I wrote had a clear purpose. Every configuration option was documented before it existed. Every error case was anticipated.

And when I worked with AI tools, the spec was the ultimate context document. Instead of spending tokens explaining what I was building in every conversation, I could point to the spec and say “build this.” The AI had the architecture, the constraints, the error handling requirements, and the phasing — all in one document.

A spec is not bureaucracy. It is the shortest path between an idea and working software.

Thats it.

Share this post