Build notes
The short version
Instead of coding every step for a specific site, the author gave an agent capabilities and goals so it could decide the next move itself.
We used to write code that specified every step. More and more, we’re building systems that give agents goals, tools, and boundaries — then let them decide the next move.
It started with a text from a friend.
"Hey, can you help me turn this kids learning site into a PDF? My daughter loves it but needs something she can scroll through offline."
Simple enough request on the surface. But this wasn’t a normal site with a nice API or clean structured data. It was an old-school interactive site. Click a link, a page loads. Click the next one, another page loads. No clean export path. No neat feed of lesson content. Just a stack of pages a kid was supposed to click through one at a time.
My friend wanted her daughter to be able to scroll through the material offline as a single PDF.
So I sat down to build it.
And I immediately caught myself following a familiar path.
I was about to write a solution for that exact site: fetch the homepage, parse the DOM using assumptions about that site’s navigation, extract the page URLs, visit each page, capture it, and stitch the captures into a PDF.
It would work. For that site.
Until the structure changed.
Until the navigation moved.
Until my friend asked about a different site.
That was the moment I stopped and asked a different question.
What if, instead of writing every decision, I gave an agent the capabilities?
The Trap of Thinking in Steps
Procedural step-by-step scripts quietly encode site-specific assumptions that break the moment the input changes.
The developer instinct is to think procedurally. Break the task down. Write the steps. Execute them.
For this problem, the classic thought process looked something like this:
1. Download the starting page
2. Parse the DOM
3. Find the links that look like lesson pages
4. Clean and order those links
5. Visit each page
6. Capture each page
7. Combine the captures into a PDF
This works. But look closely at where the fragility lives.
Even if the code is not using browser selectors directly, it still depends on assumptions about one site’s DOM structure, one site’s navigation patterns, one site’s ordering rules, and one site’s content layout.
That’s the trap.
You feel like you’re automating a workflow, but often you’re really encoding a pile of site-specific decisions that will have to be rediscovered the next time the input changes.
What My First Version Really Looked Like
The first version solved the task but hardcoded site-specific assumptions about links, ordering, and readiness directly into the code.
In the first version, I used Python requests to fetch the starting page and parsed the DOM to extract the lesson URLs. In spirit, it looked more like this:
function collect_pages_for_pdf(start_url):
download the start page HTML
parse the DOM
find all links that look like lesson/content pages
clean and normalize those links
sort them in reading order
return the ordered page URLs
function capture_pages(page_urls):
for each page URL:
open the page
wait until the content is visible
capture the page as an image
function create_scrollable_pdf(captures, output_file):
combine captures in order
save them into one PDF
That version worked, but it was still tightly coupled to how that site happened to expose its navigation.
The code wasn’t just solving the task. It was also silently carrying assumptions like:
- which links count as actual lesson pages
- how those links should be ordered
- what to ignore
- when a page is ready to capture
Those assumptions are where maintenance lives.
The Mental Model Shift
Reusable actions become tools; the site-specific decisions move into an agent's judgment instead of hardcoded logic.
The breakthrough for me was realizing that the actions were reusable even if the decisions were not.
For this task, the reusable capabilities were simple:
- extract the content links from a page
- capture a page as an image
- combine the captured pages into a PDF
That meant I could think about the problem in a very different way.
Instead of building a rigid solution for one site, I could define a small toolbox and let an agent use that toolbox to work through the site.
In pseudocode, the agent-style version looked like this:
tools = [
extract_content_links,
capture_page,
build_pdf
]
goal:
Turn this learning site into a scrollable PDF.
plan:
1. Package the core actions as reusable tools instead of hardcoded logic
2. Add resilience paths for cases where the workflow gets stuck
3. Give the agent the goal and let it orchestrate those tools to generate the PDF
Or even more simply:
from typing import Annotated
@function_tool
def extract_content_links(
start_url: Annotated[str, "Starting URL for the learning site"]
) -> list[str]:
"""Fetch the page, parse the DOM, and return lesson/content links."""
return []
@function_tool
def capture_page(
page_url: Annotated[str, "A lesson page URL to capture"]
) -> str:
"""Capture a page as an image."""
return "capture.png"
@function_tool
def build_pdf(
captures: Annotated[list[str], "Ordered page captures"],
output_file: Annotated[str, "Destination PDF file path"]
) -> str:
"""Combine captures into a single PDF."""
return output_file
agent = Agent(
name="Site to PDF Agent",
instructions=(
"Turn the learning site into a scrollable PDF. "
"Discover lesson pages, capture them in reading order, "
"and build the final PDF."
),
tools=[extract_content_links, capture_page, build_pdf],
)
That shift mattered.
The tools stayed the same.
The site-specific decisions moved into the agent’s judgment.
Old Thinking vs. Agent Thinking
Instead of hardcoding decisions for every case, write capabilities and let the system judge and adapt at runtime.
OLD THINKING
- Write the decisions
- Encode assumptions about one structure
- Predict the edge cases up front
- Rewrite the logic for the next variation
AGENT THINKING
- Write the capabilities
- Let the system inspect the current context
- Reuse the same tools across similar tasks
- Adapt the decisions at runtime
That is the mental model change.
Not: "AI will magically solve everything."
But: "Maybe I shouldn’t hardcode every branch when the real problem is judgment over changing inputs."
What an Agent Is, Really
An agent is a loop where a model repeatedly inspects state, decides, calls a tool, and observes, until the task is done.
An agent is not magic. It is a loop.
You give the model:
- a goal
- a set of tools
- boundaries on what it is allowed to do
Then the loop repeats:
- inspect the current state
- decide the next step
- call a tool
- observe the result
- continue until done
That’s it.
The interesting part is not that the loop exists. The interesting part is that the model is now handling decisions that developers used to hardcode manually.
For this PDF workflow, that meant deciding things like:
- which links looked like real content
- which ones were probably navigation chrome
- what order made sense
- when it had enough material to build the final PDF
Tools Are the Whole Game
Agent engineering shifts effort from scripting every branch to designing reusable, well-described tools with sensible constraints and recovery paths.
When you think in agents, your job changes.
You spend less time hand-authoring every branch and more time designing:
- clear tools
- useful descriptions
- sensible constraints
- strong defaults
- good recovery paths
That’s where the engineering craft moves.
A tool is not just a function. It is an action with a clean boundary and a clear purpose.
For example:
extract_content_links(start_url)
→ fetch HTML, parse the DOM, and return links that look like lesson/content pages
capture_page(page_url)
→ open the page, wait until content is ready, and capture it
build_pdf(captured_pages, output_file)
→ combine captured pages into one ordered PDF
The system does not need a different tool for every site.
It needs a reusable set of actions and enough judgment to apply them well.
Agents Are Not Magic
Agents still need developer judgment, but that judgment shifts from brittle site-specific logic to tools, examples, constraints, and fallback behavior.
This part is important.
The first version of the agent did not perfectly complete the task on its own.
I had to:
- give it examples of what “good” looked like
- improve the tool descriptions
- add constraints when it got stuck
- step in when it made weak decisions
That actually made the lesson more valuable.
The breakthrough was not that the agent replaced developer judgment.
The breakthrough was that developer judgment moved up a level.
Instead of spending all my effort on brittle site-specific branching logic, I could spend that effort on:
- better tools
- better examples
- better constraints
- better fallback behavior
That is a much more reusable place to invest engineering effort.
The Bigger Lesson
Agent thinking helps when inputs are messy, steps unknown in advance, and edge cases too numerous to enumerate cleanly in branch logic.
This pattern extends well beyond this one PDF workflow.
A lot of software problems share the same shape:
- the inputs are messy or unpredictable
- the number of steps is not known in advance
- the system has to inspect context before deciding what to do next
- edge cases are too numerous to enumerate cleanly
That is where agent thinking becomes powerful.
Not because it removes software engineering, but because it changes what software engineering is optimizing for.
Old model:
- branch logic
- exact procedures
- up-front prediction of cases
Agent model:
- reusable capabilities
- runtime judgment
- boundaries and recovery paths
But What About Control?
Agents don't remove control; control shifts from hardcoded decision trees to designing tools, boundaries, and observability.
If the agent is making decisions, are you losing control?
Not really.
You still control the parts that matter most.
- You define the tools. If there is no delete tool, nothing gets deleted.
- You define the boundaries. Same domain only. Max pages. No login flows. No payment clicks.
- You implement the tools. You still own retries, timeouts, validation, and output quality.
- You observe the loop. Good logging makes the behavior inspectable.
Agents do not remove control.
They relocate control — from hardcoded decision trees to capability design and system boundaries.
When to Think in Agents — and When Not To
Choose agent-style systems for unpredictable, judgment-heavy tasks; use traditional code for deterministic, reproducible ones.
This is not a universal pattern.
Use agent-style systems when:
- the input is unpredictable
- the path is not fully known in advance
- the work requires judgment
- the edge cases are hard to enumerate
Use traditional code when:
- the process is deterministic
- the transformation is straightforward
- reproducibility matters more than adaptation
- there is little ambiguity in the task
Turning changing interactive sites into PDFs is a great agent candidate.
Resizing an image to 800 pixels wide is not.
The Question I Ask Now
Before coding, ask whether a task is deterministic logic or a judgment problem needing reusable tools, since that decides the right architecture.
The biggest thing that changed for me was not a framework or a library.
It was a question.
Before I write brittle logic now, I pause and ask:
Is this a deterministic program? Or is this a judgment problem with reusable tools?
That question changes the architecture.
Sometimes the answer is still traditional code.
Sometimes the answer is an agent loop.
But once you start seeing the difference, it becomes hard to unsee.
Closing Thought
The real shift with agents is moving from hand-coding every decision to designing capabilities, examples, and boundaries that let systems decide on their own.
My friend’s daughter got her PDF.
More importantly, I got a new way of thinking about software.
The lesson was not that agents can magically do everything.
It was that I no longer needed to hand-author brittle logic for every variation of the same class of problem.
That is the shift I keep coming back to.
Less: writing every decision.
More: designing the capabilities, examples, and boundaries that let a system make better decisions on its own.
And for a growing class of software problems, that feels like a much better mental model.
Tags: #AI #Agents #LLM #SoftwareEngineering #DeveloperThinking #Automation
<a id="problem"></a>The Problem I Wanted to Solve
Existing VIX data sources were limited, costly, or unreliable, so a cheap, automated, long-term storage solution was needed.
As a trader and developer, I needed reliable VIX data at high resolution for:
- Options strategy analysis: VIX levels affect IV and option pricing
- Market sentiment tracking: VIX spikes indicate fear, drops indicate complacency
- Historical backtesting: Test strategies against real historical volatility data
- Real-time monitoring: Track VIX during market hours without manual checks
Existing solutions fell short:
- Yahoo Finance: Only provides 60 days of intraday data at a time
- Premium data vendors: Expensive ($50-500/month) for retail traders
- Free APIs: Rate-limited, unreliable, or missing historical depth
- Manual tracking: Too time-consuming and error-prone
I wanted a solution that:
- ✅ Collects data automatically (no babysitting)
- ✅ Stores historical data indefinitely (not just 60 days)
- ✅ Blends historical + live data seamlessly
- ✅ Costs almost nothing to run (~$1-2/month)
- ✅ Provides simple HTTP APIs for queries
<a id="vision"></a>The Vision: Automated VIX Pipeline
The goal was a simple, cheap, reliable pipeline combining Yahoo historical and Tastytrade live VIX data into monthly cloud-stored files queryable via HTTP.
The core idea was simple: build a lightweight pipeline that just works.
No complex infrastructure. No expensive databases. No babysitting. Just:
- Collect historical 5-minute VIX candles from Yahoo Finance
- Collect live VIX quotes from Tastytrade during market hours
- Normalize everything to the same OHLC format
- Store monthly partitions in Google Cloud Storage
- Query via simple HTTP functions
Design principles:
- Keep it simple: SQLite for local storage, JSON for cloud storage
- Make it cheap: GCS is ~$0.02/GB/month, Cloud Functions are nearly free
- Make it reliable: Daily collectors handle Yahoo's 60-day limit automatically
- Make it queryable: HTTP endpoints for timestamp and range queries
<a id="architecture"></a>How The Pieces Fit Together
VIX data flows through a daily collector, live pollers, monthly GCS partitions, and two query endpoints to stay cheap and fast.
OptionMantra follows a simple collection → storage → query architecture:
1. Historical 5-Minute Data (Yahoo Finance)
The Challenge: Yahoo Finance limits intraday data to 60 days per request.
The Solution: Run a daily collector that:
- Fetches the latest 60 days of 5-minute VIX candles
- Merges results into a local SQLite database
- Deduplicates by timestamp to avoid conflicts
- Over time, builds a series much longer than 60 days
SQLite Schema:
CREATE TABLE vix_5min (
datetime TEXT PRIMARY KEY,
open REAL,
high REAL,
low REAL,
close REAL,
volume INTEGER
);
Scheduling: Runs daily via Cloud Scheduler (outside market hours)
2. Live VIX Collection (Tastytrade)
Two collectors for different needs:
5-Minute Collector (mirrors historical format):
- Polls Tastytrade API during market hours
- Normalizes live quotes to OHLC format:
open, high, low, close inferred from bid/ask/last/mark
volume set to 0 (not available in live quotes)
- Stores in same SQLite table as historical data
- Uploads to GCS monthly partitions
1-Minute Collector (richer data):
- Captures detailed metadata:
- Daily high/low
- Change percentage
- Bid/ask spread
- Last trade details
- Mark price
- Stored in separate monthly partitions (
YYYY/MM_livemin.json)
- Useful for detailed analysis and debugging
Market Hours Check:
def is_market_hours():
"""Check if current time is during market hours (9:30 AM - 4:00 PM ET)"""
now = datetime.now(eastern)
return (now.weekday() < 5 and # Monday-Friday
time(9, 30) <= now.time() <= time(16, 0))
3. Cloud Storage (Google Cloud Storage)
Partitioning Strategy (keeps costs low and queries fast):
gs://vix-data-bucket/
├── 2024/
│ ├── 01.json # Historical 5-min data (Jan 2024)
│ ├── 01_live.json # Live 5-min data (Jan 2024)
│ ├── 02.json # Historical 5-min data (Feb 2024)
│ ├── 02_live.json # Live 5-min data (Feb 2024)
│ └── ...
├── 2025/
│ ├── 01.json
│ ├── 01_live.json
│ └── ...
└── live_data/
└── 2025/
└── 01_livemin.json # 1-minute granular live data
Benefits of monthly partitioning:
- Fast queries: Load only relevant months
- Low cost: GCS is ~$0.02/GB/month (entire dataset < 1GB = $0.02/month)
- Easy maintenance: Delete old files if needed
- Simple backup: Copy entire bucket with
gsutil
4. Serving Data from GCS (HTTP Endpoints)
Two Cloud Functions for querying:
a) Nearest Timestamp Query:
GET /vix/nearest?timestamp=2024-03-15T10:30:00
Response:
[
{
"datetime": "2024-03-15 10:25:00",
"open": 14.25,
"high": 14.35,
"low": 14.20,
"close": 14.30,
"volume": 0
},
{
"datetime": "2024-03-15 10:30:00",
"open": 14.30,
"high": 14.40,
"low": 14.28,
"close": 14.38,
"volume": 0
}
]
b) Date Range Query:
GET /vix/range?start=2024-03-15T09:30:00&end=2024-03-15T16:00:00
Response: [...all 5-min records in range...]
5. Bulk Upload for Historical CSVs
When you already have historical CSV data:
python upload_csv_to_gcs.py --input vix_historical.csv
Script automatically:
- Parses CSV into records
- Groups by year/month
- Uploads to correct partition in GCS
- Deduplicates existing data
<a id="data-shape"></a>The Data Shape: 5-Minute OHLC
All VIX data, historical or live, is normalized into the same 5-minute OHLC structure so it can be merged and queried consistently.
Everything is normalized to this structure so historical and live data can be merged cleanly:
{
"datetime": "YYYY-MM-DD HH:MM:SS",
"open": 14.25,
"high": 14.35,
"low": 14.20,
"close": 14.30,
"volume": 0
}
For live quotes, where we only have bid/ask/last/mark:
open = previous close or mark
high = max(bid, ask, last, mark)
low = min(bid, ask, last, mark)
close = lastormark`
volume = 0 (not available)
This keeps the series consistent and queryable.
<a id="done"></a>What Is Already Done (Highlights)
The pipeline already handles market-hours collection, clean storage, deduplication, automation, retrieval, and bulk import for about $1-2 a month.
✅ Market-hours checks: Live collectors stay quiet outside 9:30 AM - 4:00 PM ET
✅ Monthly partitioning: Clean time series storage in GCS
✅ Deduplication: No duplicate timestamps in SQLite or GCS
✅ Cloud Scheduler wiring: Fully automated collection (set it and forget it)
✅ Two retrieval APIs: Nearest timestamp + date range queries
✅ Exploration tools: Inspect full Tastytrade payload for debugging
✅ Historical CSV bulk upload: Import existing data easily
✅ 1-minute granular data: Richer live data for detailed analysis
✅ Cost optimization: ~$1-2/month total (GCS storage + Cloud Functions)
<a id="tech-stack"></a>The Tech Stack
The pipeline runs on Python with SQLite and GCS storage, using Cloud Functions and Scheduler, costing about $1-2 per month.
- Python 3.11: Main language for all scripts and functions
- SQLite: Local storage for historical and live data
- Google Cloud Storage (GCS): Cloud storage for monthly JSON partitions
- Google Cloud Functions: HTTP endpoints for queries
- Google Cloud Scheduler: Automated daily/intraday collection
- Yahoo Finance API (yfinance): Historical 5-minute candles
- Tastytrade API: Live VIX quotes during market hours
- pandas: Data manipulation and CSV handling
- pytz: Timezone handling (Eastern Time for market hours)
Infrastructure:
- Cost: ~$1-2/month (mostly GCS storage)
- Deployment: Cloud Functions (serverless, auto-scaling)
- Scheduling: Cloud Scheduler (cron-like)
- Authentication: Google Cloud service accounts
<a id="performance"></a>Performance & Cost
The pipeline runs fast enough for daily use and costs about $1-2/month, saving 96-99% versus premium data vendors.
Performance Metrics
| Metric |
Value |
| Historical Data Fetch |
~5 seconds for 60 days |
| Live Quote Fetch |
~1 second per poll |
| GCS Upload |
~2 seconds per month partition |
| Query (Nearest) |
~0.5 seconds |
| Query (Range, 1 day) |
~1 second |
| Query (Range, 1 month) |
~3 seconds |
Cost Breakdown
| Service |
Usage |
Monthly Cost |
| GCS Storage |
~500MB data |
~$0.01 |
| Cloud Functions |
~10,000 invocations |
~$0.40 (free tier) |
| Cloud Scheduler |
~50 jobs/month |
~$0.10 (free tier) |
| Egress (queries) |
~1GB/month |
~$0.12 |
| Total |
|
~$1-2/month |
vs. Premium Data Vendors: $50-500/month = 96-99% cost savings
<a id="learnings"></a>The Learnings
A simple stack of SQLite, monthly JSON partitions, and Cloud Scheduler beat complex tools and kept costs near $1/month.
What Worked Well
✅ SQLite for local storage: Simple, fast, no setup required. Perfect for single-user data collection.
✅ Monthly JSON partitions: Cheaper than BigQuery, simpler than databases, fast for time-series queries.
✅ Cloud Scheduler automation: Set it once, runs forever. No cron jobs on VPS to maintain.
✅ Tastytrade API: Free, reliable, good documentation. Much better than paid alternatives.
✅ Keep it simple: No Kafka, no Airflow, no Kubernetes. Just Python scripts + Cloud Functions.
The Challenges
⚠️ Yahoo Finance 60-day limit: Required daily collector to build longer history. Solved by merging into SQLite.
⚠️ Live data normalization: Had to infer OHLC from bid/ask/last/mark. Not perfect but good enough.
⚠️ Timezone handling: Market hours are ET, servers are UTC. Required careful pytz usage.
⚠️ Deduplication logic: Had to handle overlapping data from multiple sources. Solved with primary keys.
Key Insights
Start simple, scale later: Don't over-engineer. SQLite + GCS is enough for 99% of use cases.
Partition by time: Monthly partitions make queries fast and costs predictable.
Automate everything: Cloud Scheduler is your friend. No manual intervention needed.
Free APIs exist: Tastytrade provides excellent free APIs for retail traders.
Cost optimization matters: $1/month vs $500/month is the difference between a hobby project and an expensive subscription.
<a id="roadmap"></a>Where This Could Go Next
The planned path moves from data quality fixes to visualization, alerts, and eventually multi-symbol volatility tracking.
If I keep evolving this, these are the natural next steps:
Phase 2: Data Quality & Merging
- Merge live JSON into monthly historical files on a schedule
- Validate data integrity (no gaps, no duplicates)
- Backfill missing periods automatically
- Add data quality metrics dashboard
Phase 3: Visualization & API
- Build a small dashboard to visualize latest VIX moves
- Add RESTful API layer with authentication
- Provide CSV/JSON export endpoints
- Historical chart rendering (Plotly/Matplotlib)
Phase 4: Analysis & Alerts
- VIX spike detection (>10% move in 5 min)
- Email/SMS alerts for threshold crossings
- Historical correlation analysis (VIX vs SPX)
- Options strategy signals based on VIX levels
Phase 5: Multi-Symbol Support
- Expand beyond VIX to other volatility indices (VIX9D, VVIX)
- SPX/SPY data collection for correlation
- Individual stock IV tracking
- Crypto volatility indices
<a id="final-thoughts"></a>Final Thoughts: Build What You Need
Simple, cheap, automated tools can replace expensive data vendors and build professional-grade infrastructure for pennies.
OptionMantra represents a philosophy: solve your own problems with simple, reliable tools.
You don't need expensive data vendors. You don't need complex infrastructure. You don't need to babysit data collection scripts.
The solution? Use free APIs. Store data cheaply in cloud storage. Automate with serverless functions. Keep it simple.
This pipeline runs quietly in the background, collecting VIX data day after day, month after month. It costs less than a cup of coffee per month. It provides data whenever I need it, however I need it.
And it proves that with the right tools and a pragmatic approach, you can build professional-grade data infrastructure for pennies.
Try It Yourself
This VIX data pipeline uses free/cheap tools (Python, SQLite, GCS, Cloud Functions) to run for about $1-2/month instead of $50-500/month.
The architecture is simple. The tools are free (or dirt cheap). The code is straightforward Python.
If you're tracking VIX, options volatility, or any time-series data, this approach can work for you:
- Collect data from free APIs (Yahoo Finance, Tastytrade, etc.)
- Store locally in SQLite for quick access
- Upload to GCS in monthly partitions for long-term storage
- Query via HTTP with Cloud Functions
- Automate with Cloud Scheduler
No vendor lock-in. No expensive subscriptions. Just your data, your way.
Tech Stack: Python • SQLite • Google Cloud Storage • Cloud Functions • Yahoo Finance • Tastytrade API
Cost: ~$1-2/month (vs $50-500/month for premium vendors)
Data: 5-minute VIX resolution, indefinite historical retention
Automation: Fully automated with Cloud Scheduler
Status: ✅ Running in production
Blog: Madhudream.dev/optionmantra
Code: Available on request
<a id="screenshots"></a>Screenshots Gallery
The gallery shows the pipeline's database, cloud storage, live logs, and query API in action.
Screenshot 1: SQLite Database View
Local SQLite database with 5-minute VIX OHLC data
Screenshot 2: GCS Storage Structure
Google Cloud Storage showing monthly JSON partitions (2024/01.json, 02.json, etc.)
Screenshot 3: Live Data Collection Log
Real-time logs showing VIX quote collection during market hours
Screenshot 4: Query API Response
Sample response from nearest timestamp query endpoint
Built with Python and Google Cloud Platform. Automated data collection with Cloud Scheduler. Cost-optimized architecture for retail traders.
<a id="vision"></a>The Vision: Instant Vocabulary Capture
WordStash's core goal is to cut word-saving down to a copy-and-tap action that takes about three seconds.
The core idea behind WordStash was simple but powerful: capturing words should take seconds, not minutes.
Instead of a 5-step process to add a word, WordStash reduces it to:
- Copy the word
- Open the app
- Tap the clipboard button
- Done
Everything else happens automatically using AI.
For Readers
- See a word → Copy it → Add to WordStash → Back to reading in 3 seconds
- No typing, no searching, no manual definition entry
- AI fetches meaning and usage examples instantly
- Pronunciation with one tap (no phonetic symbols needed)
For Students
- Build your vocabulary list effortlessly
- Review with swipeable flash cards
- Filter by starred words for exam prep
- Cloud sync across all devices
<a id="how-it-works"></a>How It Works
WordStash lets you copy any new word and instantly get an AI definition, pronunciation, and synced flash card ready for review.
WordStash is built around one simple workflow:
The 3-Second Add Flow
- See a new word while reading (anywhere on your phone)
- Copy it (standard iOS copy)
- Open WordStash and tap the clipboard button
- Done (definition appears instantly)
The app automatically:
- Fetches the meaning using OpenAI's API
- Generates real-world usage examples
- Provides accurate pronunciation
- Saves to your cloud-synced collection
- Makes it available for flash card review
The Smart Features
AI-Powered Definitions: Every word gets contextually accurate definitions and multiple usage examples. The AI understands nuance and provides explanations that make sense.
Instant Pronunciation: No phonetic symbols like /ˈfəʊnətɪk/. Just tap the speaker icon and hear the word pronounced correctly using iOS's text-to-speech (works offline, multiple languages).
Smart Flash Cards: Swipeable cards with smooth animations. Filter by starred words for focused study. Track your learning progress.
Cloud Sync: Built with Supabase for real-time sync. Add a word on iPhone, review on iPad—everything stays synchronized.
<a id="features"></a>The Features: What I Built
WordStash combines AI definitions, text-to-speech, flash cards, and cloud sync into a clean, cross-device vocabulary app.
✅ AI-Powered Definitions
Every word you add gets an automatic definition and real-world usage examples. I integrated OpenAI's API to make this happen, and the results are impressively accurate.
Example:
- Word: "Ephemeral"
- Definition: "Lasting for a very short time; temporary or short-lived"
- Usage: "The ephemeral beauty of cherry blossoms makes their brief blooming season even more special"
✅ Pronunciation Made Easy
Tap the speaker icon next to any word and hear how it's pronounced. No need for phonetic symbols – just tap and listen. This uses iOS's built-in text-to-speech (AVSpeechSynthesizer), so it:
- Works offline
- Supports multiple languages
- Uses natural-sounding voices
- Adjusts speech rate for clarity
✅ Flash Cards for Review
Learning words is one thing, remembering them is another. I added swipeable flash cards with smooth flip animations:
- Swipe through your collection
- Tap to flip and reveal definitions
- Star important words
- Filter to review just starred words
- Perfect for exam prep or focused study sessions
✅ Cloud Sync
Your vocabulary collection syncs across all your devices using Supabase:
- Real-time synchronization
- Works offline, syncs when reconnected
- Secure authentication
- No data loss
- Start on iPhone, continue on iPad
✅ Beautiful Design
I went for a clean, modern interface inspired by iOS design guidelines:
- Glassmorphic cards with subtle blur effects
- Smooth animations and transitions
- Card-based layout for easy scanning
- Adaptive colors that work in light and dark mode
- No clutter, just your words and their meanings
✅ Quick Actions
- Clipboard Button: One-tap add from clipboard
- Manual Entry: Type words when needed
- Star Words: Mark favorites for priority review
- Delete/Edit: Manage your collection easily
- Search: Find words instantly across your entire collection
<a id="architecture"></a>The Architecture: SwiftUI + Cloud
WordStash combines SwiftUI, SwiftData, Supabase, and GPT-4 to deliver fast, synced, AI-powered vocabulary definitions cheaply.
WordStash follows modern iOS architecture patterns:
1. SwiftUI Interface
Modern, declarative UI framework for iOS 17+:
- Smooth animations with built-in transitions
- Native iOS components (Lists, NavigationStack, Sheets)
- Adaptive layouts for iPhone and iPad
- Dark mode support automatically
2. SwiftData Local Storage
Apple's new persistence framework (iOS 17):
- Type-safe data models
- Automatic cloud sync with CloudKit
- Efficient querying with predicates
- Seamless migration and versioning
3. Supabase Cloud Backend
Modern PostgreSQL backend with real-time capabilities:
- Row-level security for data protection
- Real-time subscriptions for instant sync
- Built-in authentication (email, OAuth)
- RESTful API with automatic TypeScript types
4. OpenAI Integration
GPT-4 for intelligent definitions:
- Contextual understanding of words
- Multiple usage examples
- Handles slang, technical terms, idioms
- Cost-effective with batching (~$0.001 per word)
<a id="tech-stack"></a>The Tech Stack
WordStash is built with SwiftUI, SwiftData, Supabase, and OpenAI's GPT-4, targeting iOS 17 and above.
For fellow developers curious about what's under the hood:
- SwiftUI: Declarative UI framework (iOS 17+)
- SwiftData: Local persistence and data modeling
- Supabase: Authentication and cloud database (PostgreSQL)
- OpenAI API: AI-generated definitions and examples (GPT-4)
- AVSpeechSynthesizer: Text-to-speech for pronunciation (built-in iOS)
- Combine: Reactive programming for data flow
- Swift Concurrency: async/await for network calls
Development Environment:
- Xcode 15+
- iOS 17+ deployment target
- Swift 5.9+
<a id="user-experience"></a>The User Experience: Speed & Simplicity
WordStash lets readers capture a word's meaning in about 3-4 seconds without leaving their reading app, then review it later with flashcards.
The Reading Flow (No Interruption)
Reading article on Safari
↓
See unfamiliar word: "serendipity"
↓
Long-press → Copy
↓
Switch to WordStash (1 sec)
↓
Tap clipboard button (1 sec)
↓
Definition appears with AI-generated examples (1 sec)
↓
Switch back to Safari (1 sec)
↓
Total time: 3-4 seconds
The Review Flow (Focused Learning)
Open WordStash
↓
Navigate to Flash Cards
↓
Filter by "Starred" (exam prep mode)
↓
Swipe through cards
↓
Tap to flip and test yourself
↓
Star difficult words for re-review
<a id="who-its-for"></a>Who It's For
WordStash serves readers, students, professionals, English learners, and word enthusiasts who want to build and retain vocabulary.
WordStash is perfect if you:
📚 Avid Readers
- Encounter new words regularly in books, articles, blogs
- Want to retain and remember interesting vocabulary
- Prefer seamless integration with reading flow
🎓 Students
- Studying for standardized tests (SAT, GRE, TOEFL, IELTS)
- Building academic vocabulary
- Need efficient review tools (flash cards, starred words)
- Want to track learning progress
💼 Professionals
- Improving professional vocabulary
- Reading industry-specific content
- Communicating more effectively
- Staying sharp with continuous learning
🌍 English Learners
- Learning English as a second language
- Building practical vocabulary
- Need pronunciation help
- Want real-world usage examples
🧠 Word Enthusiasts
- Love learning new words
- Enjoy expanding vocabulary
- Appreciate elegant language
- Like collecting interesting terms
<a id="roadmap"></a>What's Next: The Roadmap
WordStash is at version 1.0, with future phases planned for smart learning, customization, and social features.
This is version 1.0, and I have exciting features planned:
Phase 2: Smart Learning
- Spaced repetition algorithm: Review words at optimal intervals
- Progress tracking: Visualize your vocabulary growth
- Learning streaks: Stay motivated with daily goals
- Mastery levels: Track which words you've truly learned
Phase 3: Enhanced Features
- Widget support: "Word of the day" on your home screen
- Export functionality: Download your vocabulary as CSV/PDF
- Word families: See related words (synonyms, antonyms, derivatives)
- Etymology: Learn word origins and historical usage
Phase 4: Customization
- Custom themes: Choose colors that match your style
- Card layouts: Multiple flash card designs
- Categories: Organize words by topic (business, literature, etc.)
- Import from reading apps: Auto-detect highlighted words
Phase 5: Social & Sharing
- Shared collections: Study with friends
- Public word lists: Explore curated vocabularies
- Achievements: Badges for milestones
- Challenge mode: Compete with yourself or friends
<a id="final-thoughts"></a>Final Thoughts: Build Your Vocabulary Effortlessly
WordStash removes friction from vocabulary building by using AI and native iOS features to capture words instantly without breaking reading flow.
WordStash represents a philosophy: learning should be frictionless.
You shouldn't need to break your reading flow to look up a word. You shouldn't need to manually type definitions. You shouldn't struggle with phonetic symbols to learn pronunciation.
The solution? Use AI to eliminate friction. Use native iOS features for seamless integration. Build a beautiful, fast interface that respects your time.
This app is my daily companion for building vocabulary. Every interesting word I encounter goes straight into WordStash. Within seconds, I have the definition, examples, and pronunciation—all without breaking my reading flow.
And now it can be yours too.
Try It Yourself
WordStash is currently available only through TestFlight while the author seeks App Store approval, and welcomes feedback by email.
WordStash is available on the Testflight. Please email me at madhured@gmail.com, happy to take your feedback. I am working to get approval to get it avaialble for every one on App Store.
Whether you're a student, professional, avid reader, or just someone who loves words like I do, I hope WordStash becomes your go-to tool for building an impressive vocabulary.
🔗 Download on App Store: [Coming Soon]
Questions? Feedback? Reach me at madhu@madhudream.dev
Tech Stack: SwiftUI • SwiftData • Supabase • OpenAI API • AVSpeechSynthesizer
Platform: iOS 17+
Languages: Swift 5.9+
Features: AI definitions • Cloud sync • Flash cards • Pronunciation
Status: ✅ Version 1.0 Released
Blog: Madhudream.dev/wordstash
Download: App Store (Coming Soon)
<a id="screenshots"></a>Screenshots Gallery
The app shows word lists, AI definitions, clipboard add, flash cards, and starred word filtering across five screenshots.
Screenshot 1: Main Word List
Your vocabulary collection at a glance - clean card-based layout with word, definition, and quick actions
Screenshot 2: Word Detail View
AI-generated definition with pronunciation button and usage examples
Screenshot 3: Add Word from Clipboard
One-tap add from clipboard - the fastest way to capture new words while reading
Screenshot 4: Flash Cards Mode
Swipeable flash cards with smooth animations - perfect for study sessions
Screenshot 5: Starred Words Filter
Filter by starred words for focused exam prep and priority review
Built with SwiftUI and modern iOS development practices. AI-powered definitions using OpenAI GPT-4. Cloud sync with Supabase PostgreSQL backend.
P.S. – Every word in this post was one I already knew, but if I encountered a new one while writing, you bet I would've added it to WordStash! 😊
<a id="what-is-sdd"></a>What is Spec-Driven Development?
Spec-driven development flips the focus from writing code to writing detailed specs, letting AI implement while humans validate each phase.
Traditional development flow:
Idea → Code → Test → Refactor → Deploy
Spec-driven development flow:
Idea → Comprehensive Spec → AI Implementation → Human Validation → Deploy
The key difference? You spend more time defining WHAT to build than HOW to build it.
Core Principles
- Specs are the source of truth - Not the code, not the comments, not verbal discussions
- AI generates implementation - From detailed requirements, not vague prompts
- Human architects, AI implements - You define structure, AI fills it in
- Phase-by-phase validation - Test after each logical chunk, not at the end
- Requirements clarity is the bottleneck - Not coding speed
What Makes It "Spec-Driven"?
You write:
- User stories with acceptance criteria
- API contracts with request/response schemas
- Data models with field descriptions
- Technical plans with file structures
- Edge cases and error scenarios
- Task breakdowns with dependencies
AI writes:
- TypeScript/React/Python code
- Unit and integration tests
- API endpoint implementations
- Database schemas
- Configuration files
- Documentation
You validate:
- Architecture remains sound
- Edge cases are handled
- Performance is acceptable
- Security is not compromised
<a id="spec-kit"></a>The Spec-Kit Methodology
Spec-Kit splits AI-assisted development into four phases—Specify, Plan, Tasks, Implement—with humans validating each step before code generation.
I developed using github's Spec-Kit approach with four phases:
┌─────────────────────────────────────────────────────────────────┐
│ SPEC-KIT WORKFLOW │
└─────────────────────────────────────────────────────────────────┘
Phase 1: SPECIFY (1-2 hours)
↓
Human writes comprehensive spec
- User stories with acceptance criteria
- Functional requirements (in scope / out of scope)
- Edge cases and error handling
- Success metrics
↓
Output: specs/###-feature-name/spec.md
Phase 2: PLAN (30 min - 1 hour)
↓
AI generates technical implementation plan
- Tech stack decisions
- File structure
- Component hierarchy
- API contracts
- Data models
↓
Human reviews and approves architecture
↓
Output: specs/###-feature-name/plan.md
Phase 3: TASKS (30 min)
↓
AI breaks plan into 30-50 actionable tasks
- Sequential vs parallel execution
- File paths for each task
- Dependencies mapped
- Checkpoints defined
↓
Human validates task breakdown
↓
Output: specs/###-feature-name/tasks.md
Phase 4: IMPLEMENT (8-12 hours)
↓
AI generates code from tasks
- Phase-by-phase execution
- Human validates after each phase
- Fix issues before proceeding
- Build and test continuously
↓
Output: Working, production-ready code
Spec Structure
Every feature spec lives in specs/{###-feature-name}/:
specs/
├── 001-contentflow-cms/
│ ├── spec.md # User stories, requirements, edge cases
│ ├── plan.md # Tech stack, architecture, file structure
│ ├── tasks.md # 30-50 actionable tasks with dependencies
│ ├── data-model.md # Entity definitions and relationships
│ └── contracts/ # API specs, test requirements
├── 002-unified-deployment/
│ ├── spec.md
│ ├── plan.md
│ └── tasks.md
├── 003-ai-translation/
│ └── ...
└── 004-ai-tooltip/
└── ...
<a id="the-good"></a>The Good: Massive Productivity Gains
Spec-driven AI development built ContentFlow CMS in 32 hours instead of an estimated 110, a roughly 3.4x personal productivity gain.
Real Numbers from ContentFlow CMS
Development time:
- Manual estimate: 2-3 weeks (80-120 hours)
- With Spec-Kit: 2 days (32 hours)
- Speedup: 4-10x faster
Code generated:
- Lines of code: ~15,000 lines
- AI-generated: ~13,500 lines (90%)
- Human-written: ~1,500 lines (specs + fixes)
What AI built without issues:
- TypeScript interfaces (100+ types)
- React components (50+ components)
- Zustand store logic
- Express.js API routes
- Pulumi infrastructure code
- Unit test scaffolding
- File upload logic
- JSON parsing and validation
Where AI Excelled
✅ Boilerplate code - No more writing reducers, actions, selectors manually
✅ Repetitive patterns - Form components, CRUD endpoints, similar across files
✅ Type definitions - Perfect TypeScript interfaces from data models
✅ API implementations - Request validation, error handling, response formatting
✅ Test scaffolding - Unit test structure (human fills in assertions)
✅ Configuration files - package.json, tsconfig.json, vite.config.ts
✅ Documentation - JSDoc comments, README files, inline explanations
The Productivity Multiplier
I spent:
- 4 hours writing specs (user stories, requirements, edge cases)
- 28 hours on AI implementation + validation + fixes
- Total: 32 hours for a production-ready system
Without AI, I estimate:
- 20 hours planning and designing
- 60 hours implementing core features
- 20 hours writing tests
- 10 hours documentation
- Total: 110 hours minimum
Personal productivity gain: 3.4x
But the real gain? I didn't have to context-switch between architectural thinking and implementation details. I could stay in "architect mode" while AI handled the mechanical translation of specs into code.
<a id="challenges"></a>The Challenges: Model Fatigue & Limitations
Long AI sessions degrade into sloppy, stubborn, architecture-violating, over-engineered code unless the developer intervenes constantly.
Let me be brutally honest: Spec-driven development is not magic. It has real, frustrating limitations.
1. Model Fatigue ("Napping" and "Yawning")
What is it?
- Model "napping": Slow, lazy responses toward the end of long sessions
- Model "yawning": Repetitive, generic output as conversation grows
- Context pollution: Quality degrades with massive conversation history
Real example from my build:
Hour 1-8: AI is sharp, generates clean code, catches edge cases
// Generated flawlessly
interface ContentFlowConfig {
appId: string;
language: string;
storageUrl: string;
pages: string[];
}
Hour 16-20: AI starts to slip, makes obvious mistakes
// Generated with bugs - missing null checks
function getContent(contentId: string) {
return store.contentMap[contentId]; // ❌ What if contentMap is undefined?
}
Hour 24-28: AI gets lazy, takes shortcuts
// Generated incomplete code
function uploadImage(file: File) {
// TODO: Implement image upload
return Promise.resolve("/path/to/image");
}
Why it happens:
- Long conversation = huge context window = slower thinking
- Model starts optimizing for speed over correctness
- Earlier mistakes compound into later code
- Context becomes noisy with iterations and fixes
How I handled it:
- Fresh chat windows: Started new conversations every 4-6 hours
- Clear context: Gave AI only relevant files, not entire history
- Explicit instructions: "Read X file, implement Y exactly as specified"
- Human intervention: When quality dropped, I coded it myself
2. Resistance to Fixes ("The Stubborn Model")
What is it?
AI sometimes refuses to fix obvious bugs, even when pointed out explicitly.
Real example:
Me: "The icon type is not updating when I save changes. Please send the updated iconType in the postMessage."
AI (1st attempt): Adds console.log, doesn't actually fix it
Me: "Still broken. The icon doesn't change. Look at line 147 in InputHelpElement.ts"
AI (2nd attempt): Refactors unrelated code, still doesn't fix it
Me: "Stop. Read the handleSave function. It's not sending iconType in the message. Add it."
AI (3rd attempt): Finally fixes it
Why it happens:
- AI doesn't always understand the root cause
- AI sometimes fixates on the wrong part of code
- AI may not have full context of how components interact
- AI prefers to add code rather than modify existing logic
How I handled it:
- Be extremely specific: "Line 147, variable iconType, add it to postMessage payload"
- Show code directly: Paste the exact function that needs changing
- Break the loop: If AI fails 3 times, I fix it myself
- Validate immediately: Test after every change, catch regressions early
3. Architecture Drift
What is it?
AI can violate architectural principles you defined earlier if not reminded constantly.
Real example from ContentFlow:
Constitution rule: "No cross-app imports. Apps communicate only through SDK."
AI generates (Hour 22):
// apps/cms/src/utils/helpers.ts
import { FormField } from '../../../client-tax-forms/src/types'; // ❌ VIOLATION
Why it happens:
- AI doesn't maintain long-term architectural memory
- Convenience wins over correctness
- AI doesn't always read constitution.md
- Spec constraints get lost in long conversations
How I handled it:
- Constitution file: Created
.github/copilot-instructions.md with core rules
- Repeat constraints: Remind AI of key principles in each phase
- Code reviews: Scan for violations after each checkpoint
- Automated checks: ESLint rules to catch cross-app imports
4. Over-Engineering
What is it?
AI sometimes adds unnecessary complexity when simple solutions exist.
Real example:
Spec: "Store content in JSON files, one per page per language"
AI generates:
// ❌ Over-engineered - created a whole versioning system
class ContentVersionManager {
private versions: Map<string, ContentVersion[]>;
saveVersion(content: Content) {
// 50 lines of versioning logic we don't need yet
}
}
What I actually needed:
// ✅ Simple - just save the JSON
function saveContent(content: Content) {
fs.writeFileSync(`${appId}-${pageId}-${lang}.json`, JSON.stringify(content));
}
Why it happens:
- AI trained on complex codebases
- AI assumes you want "production-grade" patterns
- AI doesn't know your phase 1 vs phase 2 scope
How I handled it:
- Explicit simplicity: "Use the simplest approach. No abstractions unless specified."
- KISS principle: Added to constitution: "No premature optimization"
- Review generated code: Delete unnecessary complexity after each phase
<a id="when-works"></a>When It Works, When It Fails
Spec-driven AI development succeeds in well-understood, modular, stable-requirement domains but fails with legacy code, ambiguity, security, or performance-critical work.
✅ Spec-Driven Development Works When:
1. Problem domain is well-understood
- CMS, CRUD apps, data pipelines, APIs
- You've built something similar before
- Industry has established patterns
2. Requirements are stable and comprehensive
- You know exactly what you want
- Edge cases documented upfront
- Acceptance criteria clear
3. Architecture is modular
- Components are loosely coupled
- Clear interfaces between modules
- Independent deployment units
4. Tech stack is predetermined
- You chose React, TypeScript, Bun, Azure upfront
- AI doesn't need to make stack decisions
- Frameworks are well-documented
5. Human architect validates each phase
- You review after every 10-15 tasks
- You catch issues before they compound
- You maintain mental model of system
❌ Spec-Driven Development Fails When:
1. Legacy codebase with tight coupling
- AI can't understand complex dependencies
- Changes break unrelated parts
- Refactoring is risky without human judgment
2. Ambiguous or changing requirements
- You're still figuring out what to build
- Requirements emerge during development
- Customer feedback changes direction
3. Complex domain logic
- Financial calculations requiring expertise
- Regulatory compliance with nuances
- Business rules with exceptions and edge cases
4. Security-critical systems
- Authentication, authorization, encryption
- AI may miss security vulnerabilities
- Human security review is mandatory
5. Performance-critical code
- AI doesn't optimize for latency/throughput
- Requires profiling and benchmarking
- Domain-specific optimizations needed
<a id="metrics"></a>Real Metrics from ContentFlow CMS
Let me share the actual numbers from building ContentFlow over 2 days: Time Breakdown (32 hours total) Activity Hours Percentage Writing specs 4 12.5% AI code generation 16 50% Human validation & testing 8 25% Bug fixes (human) 3 9.4%…
Let me share the actual numbers from building ContentFlow over 2 days:
Time Breakdown (32 hours total)
| Activity |
Hours |
Percentage |
| Writing specs |
4 |
12.5% |
| AI code generation |
16 |
50% |
| Human validation & testing |
8 |
25% |
| Bug fixes (human) |
3 |
9.4% |
| Deployment setup |
1 |
3.1% |
Code Generation Breakdown
| Component |
Lines of Code |
AI % |
Human % |
| SDK (packages/sdk) |
3,500 |
95% |
5% |
| CMS (apps/cms) |
5,000 |
90% |
10% |
| Server (apps/server) |
2,500 |
85% |
15% |
| client Tax Forms |
2,000 |
95% |
5% |
| Pulumi Infrastructure |
1,000 |
80% |
20% |
| Tests |
1,000 |
70% |
30% |
| Total |
15,000 |
90% |
10% |
Issue Resolution
| Issue Type |
Occurrences |
AI Fixed |
Human Fixed |
| Type errors |
45 |
40 |
5 |
| Missing null checks |
12 |
8 |
4 |
| API contract violations |
6 |
2 |
4 |
| Performance issues |
3 |
0 |
3 |
| Security concerns |
2 |
0 |
2 |
| Architecture violations |
5 |
1 |
4 |
Productivity Comparison
| Metric |
Manual |
With Spec-Kit |
Improvement |
| Dev time |
110 hours |
32 hours |
3.4x faster |
| Bugs in production |
~15-20 (estimate) |
7 |
Better quality |
| Test coverage |
~60% (typical) |
80% |
Higher |
| Documentation |
Sparse |
Comprehensive |
Much better |
<a id="workflow"></a>The Workflow in Practice
ContentFlow was built in two days by moving from specs to plan to 90 generated tasks, with AI handling most fixes and humans stepping in for a few stubborn bugs.
Here's how I actually used Spec-Kit to build ContentFlow, hour by hour:
Day 1: Specs → Implementation
Hours 1-4: Writing Specs
specs/001-contentflow-cms/spec.md
✓ User Story 1: Edit text on a page (P1)
✓ User Story 2: SDK integration (P1)
✓ User Story 3: Multi-app support (P2)
✓ User Story 4: client metadata forms (P2)
✓ User Story 5: Image editing (P3)
✓ Acceptance criteria for each story
✓ Edge cases: malformed JSON, missing files, network errors
✓ Success metrics: <2s edit-to-display, <15KB SDK bundle
Hours 5-6: Generate Plan
Prompt: "/speckit.plan"
AI generates:
✓ Tech stack: React, TypeScript, Zustand, Vite, Bun
✓ File structure: packages/sdk, apps/cms, apps/demo, data/
✓ Component hierarchy: ContentComponent, EditorPanel, PreviewPanel
✓ API contracts: GET/POST /api/content/:filename
✓ Data model: ContentMap, ContentFile, AppConfig
Hours 7-8: Generate Tasks
Prompt: "/speckit.tasks"
AI generates:
✓ Phase 1: Setup (11 tasks)
✓ Phase 2: SDK Core (26 tasks) ← BLOCKING
✓ Phase 3: React Components (15 tasks)
✓ Phase 4: CMS UI (18 tasks)
✓ Phase 5: Server (12 tasks)
✓ Phase 6: Integration (8 tasks)
Total: 90 tasks across 6 phases
Hours 9-16: AI Implementation (Phase 1-3)
Phase 1: Setup
✓ Create monorepo structure
✓ Add package.json, turbo.json, tsconfig
✓ Create data/ directory with apps.config.json
Validation: ✓ npm install works
Phase 2: SDK Core (CRITICAL)
✓ Create types (ContentMap, ContentFlowConfig)
✓ Create Zustand store
✓ Create LocalJsonAdapter
✓ Create ContentFlowSDK.initialize()
Validation: ✓ SDK builds, exports correct API
Issue: Missing null checks in getContent() → AI fixed
Phase 3: React Components
✓ Create ContentComponent
✓ Create useContent hook
✓ Handle loading states
Validation: ✓ Component renders defaultText, hydrates async
Issue: Type error in props → AI fixed
Hours 17-20: CMS UI + Server
Phase 4: CMS UI
✓ Create PreviewPanel with iframe
✓ Create EditorPanel with text input
✓ Implement postMessage communication
Validation: ✓ Click element → open editor
Issue: Preview not updating → Human fixed (AI tried 3 times)
Phase 5: Server
✓ Create Bun server with Express
✓ Add GET /api/content/:filename
✓ Add POST /api/content/:filename
✓ Add image upload endpoint
Validation: ✓ Server serves JSON, CMS saves content
Issue: CORS not configured → AI fixed
Day 2: Deployment + Polish
Hours 21-28: Deployment Infrastructure
specs/002-unified-deployment/
✓ Write spec for Azure Container Apps
✓ AI generates Pulumi infrastructure code
✓ Multi-app routing (/cms, /client, /demo)
✓ Blob storage integration
Validation: ✓ pulumi preview shows correct resources
Issue: Managed Identity not configured → Human fixed
Deploy: pulumi up
✓ Container Apps created
✓ Blob storage provisioned
✓ CDN endpoint configured
Status: ✅ DEPLOYED
Hours 29-32: Testing + Bug Fixes
Manual Testing:
✓ Edit text in CMS → saves to JSON
✓ Refresh demo app → shows updated text
✓ Upload image → optimized, uploaded to blob
✓ Language switching → loads correct locale
Bugs Found:
1. Icon not updating after save → Human fixed
2. Preview iframe CORS issue → AI fixed
3. Image upload timeout → Human increased limit
4. Translation batch not processing → AI fixed
Final Validation:
✓ All 4 apps running on Azure
✓ CMS editing works end-to-end
✓ Multi-language support functional
Status: ✅ PRODUCTION READY
<a id="learnings"></a>Learnings & Best Practices
Specs must be concrete and detailed, like instructions for a junior developer, or the AI produces vague code.
After building ContentFlow with Spec-Kit, here's what I learned:
1. Write Specs Like You're Explaining to a Junior Developer
❌ Bad spec:
"The CMS should allow editing content"
✅ Good spec:
"When a CMS user hovers over an element with data-content-id attribute in the preview iframe, the element highlights. When clicked, the EditorPanel opens on the right side with the current content value pre-filled. Changes are saved to data/{appId}-{pageId}-{lang}.json via POST /api/content/:filename. If save fails, show inline error notification."
Why: AI needs concrete details. Vague specs → vague code.
2. Define Data Models Before Any Code
Always create data-model.md first:
ContentFile
Field Type Required Description $meta ContentMetadata Yes File metadata [contentId] string No Content value for contentId
| Field |
Type |
Required |
Description |
| $meta |
ContentMetadata |
Yes |
File metadata |
| [contentId] |
string |
No |
Content value for contentId |
ContentMetadata
Field Type Required Description appId string Yes Application identifier pageId string Yes Page identifier lang string Yes Locale (e.g., "en-US") version number Yes Content version number updatedAt string Yes ISO timestamp **Why:** AI generates perfect TypeScript interfaces from tables.
| Field |
Type |
Required |
Description |
| appId |
string |
Yes |
Application identifier |
| pageId |
string |
Yes |
Page identifier |
| lang |
string |
Yes |
Locale (e.g., "en-US") |
| version |
number |
Yes |
Content version number |
| updatedAt |
string |
Yes |
ISO timestamp |
**Why:** AI generates perfect TypeScript interfaces from tables. Saves hours of type definition.
### 3. Use Phase-by-Phase Validation
**Don't let AI run for 8 hours straight.**
Instead:
Phase 1: Setup (10 tasks) → STOP → Validate → Fix issues
Phase 2: SDK Core (15 tasks) → STOP → Validate → Fix issues
Phase 3: React Components (12 tasks) → STOP → Validate → Fix issues
**Why:** Issues compound. A bug in Phase 1 becomes 10 bugs in Phase 3. Catch early.
### 4. Keep a Constitution File
Create `.github/copilot-instructions.md` with core rules:
```markdown
Architectural Rules
Library-First: SDK has zero framework imports in core No Cross-App Imports: Apps communicate only through SDK Storage Abstraction: All I/O through IContentStorage Content-ID as Contract: Never remove/rename contentId
- Library-First: SDK has zero framework imports in core
- No Cross-App Imports: Apps communicate only through SDK
- Storage Abstraction: All I/O through IContentStorage
- Content-ID as Contract: Never remove/rename contentId
Coding Principles
Keep code simple and specific, watch for AI fatigue, and test every change immediately to catch regressions.
- SOLID: Single responsibility per module
- KISS: No abstraction without concrete use case
- Flat JSON: No deep nesting in content files
- Default Values: Every ContentComponent has defaultText
**Why:** AI forgets. Constitution reminds it of key constraints.
### 5. Handle Model Fatigue Proactively
**Signs your model is tired:**
- Responses are slower
- Code quality drops
- AI misses obvious bugs
- Same mistakes repeat
**What to do:**
1. **Start fresh chat**: Copy only relevant context
2. **Switch models**: Claude → GPT-4 → Claude
3. **Take a break**: Let conversation reset
4. **Human intervention**: Code it yourself when quality drops
### 6. Be Extremely Specific for Bug Fixes
**❌ Bad bug report:**
> "The icon doesn't update"
**✅ Good bug report:**
> "File: `packages/sdk/src/webcomponent/InputHelpElement.ts`, Line 147. The `handleSave` function sends a postMessage to CMS but doesn't include the `iconType` field. Add `iconType: this.iconType` to the message payload so the icon updates in real-time."
**Why:** Vague → AI guesses wrong. Specific → AI fixes correctly.
### 7. Test Immediately After Each Change
**Set up hot reload:**
```bash
# Terminal 1: SDK hot rebuild
npm run dev --workspace=packages/sdk
# Terminal 2: App hot reload
npm run dev --workspace=apps/cms
# Validate in browser after every AI change
Why: Catch regressions immediately. Don't accumulate broken code.
<a id="pros-cons"></a>Pros & Cons: The Honest Truth
Spec-driven development with AI gives large speed and consistency gains but still demands heavy human validation, upfront spec time, and careful review for quality, security, and performance.
Let me be completely transparent about spec-driven development:
✅ Pros
1. Massive productivity gains (3-10x) for well-scoped problems
- I built ContentFlow in 2 days vs 2-3 weeks manual
- AI handles boilerplate, letting me focus on architecture
- 90% of code generated automatically
2. Comprehensive documentation as a side effect
- Specs become living documentation
- Every requirement is written down
- New team members can read specs to understand system
3. Higher test coverage
- AI generates test scaffolding
- Specs include test requirements
- Edge cases documented upfront
4. Consistent code quality
- AI follows patterns strictly
- No "I'll refactor this later" shortcuts
- TypeScript types are complete
5. Reduced context switching
- Stay in "architect mode" throughout
- Don't need to drop into implementation details
- Maintain big-picture thinking
6. Easier onboarding
- Specs act as requirements docs
- New developers understand "why"
- Less tribal knowledge
❌ Cons
1. Upfront spec time (2-4 hours per feature)
- Requires detailed thinking before coding
- Can feel slow if you want to "just code"
- Learning curve for writing good specs
2. Model fatigue is real
- Quality drops after long sessions
- Need to restart conversations
- Some bugs require human intervention
3. AI doesn't understand context deeply
- Misses subtle requirements
- Can violate architecture if not reminded
- Doesn't always fix bugs on first try
4. Over-engineering tendency
- AI adds complexity you don't need
- Need to actively simplify generated code
- Review overhead increases
5. Not suitable for exploratory work
- Hard to spec when you don't know what you want
- Requires stable requirements
- Changing specs mid-implementation is painful
6. Human validation is mandatory
- Can't just "let AI run"
- Need to review after each phase
- Testing burden same as manual dev
7. Security and performance not guaranteed
- AI may miss vulnerabilities
- Doesn't optimize for latency
- Human review critical for production
<a id="recommendations"></a>Recommendations for Using Spec-Kit
Master Level 3 (human-reviewed AI coding) first, and reserve Level 4 or Spec-Kit for well-scoped, stable, greenfield work.
Based on my experience, here's my advice:
Start with Level 3, Not Level 4
Level 3 (Code Manager):
- Human defines "what" and "why"
- AI generates "how"
- Human reviews every change in real-time
- Better for most projects
Level 4 (Requirement-Driven):
- Human writes comprehensive specs
- AI implements entire features
- Human validates at checkpoints
- Use selectively for well-scoped work
My recommendation: Master Level 3 first. Use Level 4 only for:
- Greenfield projects with clear scope
- Features similar to ones you've built before
- Tight deadlines with stable requirements
- Boilerplate-heavy work (CRUD, forms, configs)
When to Use Spec-Kit
✅ Use Spec-Kit for:
- Greenfield projects (start fresh)
- Well-understood domains (CMS, CRUD, APIs)
- Modular architecture (loose coupling)
- Stable requirements (no moving targets)
- Deadline pressure (need speed)
❌ Don't use Spec-Kit for:
- Legacy refactoring (too complex)
- Exploratory prototyping (requirements unclear)
- Security-critical code (needs expert review)
- Performance optimization (needs profiling)
- Complex domain logic (requires expertise)
Best Practices
- Invest in good specs - 4 hours on specs saves 20 hours debugging
- Validate after every phase - Don't let AI run unsupervised
- Keep conversations short - Fresh chat every 4-6 hours
- Be specific for bug fixes - File, line number, exact change
- Maintain a constitution - Document core architectural rules
- Test continuously - Hot reload, validate immediately
- Know when to intervene - Don't fight AI for 2 hours, just code it
- Use version control - Commit after each validated phase
<a id="final-thoughts"></a>Final Thoughts: The Future of Development
Spec-driven development shifts developers from writing code to architecting systems while AI handles implementation, enabling much faster building.
Spec-driven development represents a fundamental shift in how we build software.
We're moving from:
- Coding as the primary skill → Specification as the primary skill
- Implementation speed as the bottleneck → Requirements clarity as the bottleneck
- Writing code → Architecting systems
- Junior devs write code → AI writes code, seniors architect
This doesn't mean developers become obsolete. It means our role evolves:
- From coders to architects
- From implementers to validators
- From bug fixers to system designers
- From code writers to requirement definers
The winning combination:
- Human: Strategy, architecture, requirements, validation
- AI: Implementation, boilerplate, tests, documentation
ContentFlow CMS proves this works. I built a production-ready system in 2 days by focusing on WHAT to build and letting AI figure out HOW.
But I also learned the limits. AI gets tired. AI makes mistakes. AI needs guidance. Human judgment remains essential.
The future isn't "AI replaces developers." It's "developers + AI build 10x faster."
Spec-driven development is one way to get there. Not the only way. But a damn effective one.
Try It Yourself
Spec-driven development lets you start small, write detailed specs, and use AI to generate most of the code, potentially cutting build time from weeks to days.
The methodology is clear. The tools are available. The results speak for themselves.
If you want to try spec-driven development:
- Start small - Pick one feature, not an entire app
- Write a detailed spec - User stories, acceptance criteria, edge cases
- Use AI to generate implementation - Claude, GPT-4, Copilot
- Validate continuously - Test after every logical chunk
- Know when to intervene - Fix issues manually when AI struggles
- Iterate and learn - Improve your spec-writing skills
The learning curve is real. The upfront work is significant. The benefits are undeniable.
You might build your next feature in 2 days instead of 2 weeks.
Methodology: Spec-Kit (Specify → Plan → Tasks → Implement)
Development Time: 2 days (32 hours) for ContentFlow CMS
AI Contribution: 90% of code generated from specs
Productivity Gain: 3-10x faster than manual development
Best For: Greenfield projects, well-defined domains, modular architectures
Not For: Legacy refactoring, exploratory work, security-critical code
Status: ✅ Production-deployed on Azure
Blog: madhudream.dev/spec-driven-development
Repository: github.com/madhudream/contentflow-cms
Built with AI assistance using Level 4 spec-driven development. 90% of code AI-generated from comprehensive specifications. Architecture, specifications, and validation by human engineer. This blog post itself written by AI from outline and context provided by human.
<a id="vision"></a>The Vision: Native Framework Integration
ContentFlow aims to be a native SDK that frameworks include directly, letting product teams edit live content without developer help.
The core idea behind ContentFlow CMS was simple but powerful: work WITH frameworks, not against them.
Instead of running parallel to your app like third-party tools, ContentFlow would be a native library that integrates seamlessly into your codebase. Think of it as an SDK that developers include just like any other package—except this one gives product teams superpowers.
Here's what I envisioned:
For Developers
import { ContentComponent } from '@contentflow/sdk/react';
<ContentComponent
contentId="hero-title"
defaultText="Welcome to ContentFlow"
data-content-id="hero-title"
/>
That's it. One component replaces your static text. It works with React hooks, Angular services, or vanilla JavaScript. No performance overhead. No framework conflicts. Just clean, framework-native integration.
For Product Teams
A beautiful CMS interface where you can:
- See a live preview of your app in an iframe
- Click on any text element to edit it
- Switch between languages instantly
- Hit "Translate All" to bulk-translate content using AI
- Publish changes that go live immediately
No code changes. No developer involvement. No deployment pipelines.
<a id="architecture"></a>The Architecture: Three Simple Layers
ContentFlow runs on a visual CMS portal, one unified Bun/Azure server, and a zero-dependency framework-agnostic SDK.
I designed ContentFlow around three core components:
1. The CMS Portal (React + Tailwind)
A state-of-the-art visual editor inspired by modern design tools. You hover over elements in the preview iframe, and they highlight. Click the pencil icon, edit in the sidebar panel, save—and watch your changes appear instantly.
2. The Unified Server (Bun + Azure)
Instead of running 5 separate dev servers, I built one consolidated Bun server that:
- Hosts all apps under a single domain (
/cms, /client, /demo, /portal)
- Stores content in simple JSON files during development
- Uploads to Azure Blob Storage for production
- Scales from 0 to 3 replicas based on traffic (scale-to-zero = near-zero cost)
- Optimizes uploaded images automatically using Sharp
3. The Framework-Agnostic SDK
The heart of the system. Zero dependencies in the core. Works with:
- React:
<ContentComponent> with hooks
- Angular:
<content-component> Web Component
- Any framework: Custom adapters follow the same pattern
The SDK loads all content in a single JSON file per page (not 100 individual API calls like third-party tools). Content hydrates asynchronously in the background while displaying default text synchronously—zero flash of empty content.
<a id="arch-comparison"></a>Architecture Comparison: Why ContentFlow is Faster
ContentFlow uses native framework components and a single JSON fetch instead of a parallel-rendering script, making pages 10-25x faster.
Here's a visual breakdown of how the two approaches differ fundamentally:
┌─────────────────────────────────────────────────────────────────────────┐
│ ❌ THIRD-PARTY TOOLS (Walkme/Pendo) - Parallel Rendering │
└─────────────────────────────────────────────────────────────────────────┘
Browser Window
┌─────────────────────────────────────────────────────────────┐
│ │
│ ┌─────────────────────────────────────────────┐ │
│ │ Your React/Angular App │ │
│ │ ┌──────────────────────────────────────┐ │ │
│ │ │ Component Tree │ │ │
│ │ │ Virtual DOM / Change Detection │ │ │
│ │ │ Renders: Buttons, Forms, Text │ │ │
│ │ └──────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────┘ │
│ ↓ │
│ ⚠️ CONFLICT ZONE ⚠️ │
│ ↓ │
│ ┌─────────────────────────────────────────────┐ │
│ │ Third-Party Script (200-500KB) │ │
│ │ ┌──────────────────────────────────────┐ │ │
│ │ │ • Scans entire DOM tree │ │ │
│ │ │ • Re-renders elements in parallel │ │ │
│ │ │ • Overrides framework lifecycle │ │ │
│ │ │ • Makes 10-50 API calls │ │ │
│ │ │ • Loads external scripts │ │ │
│ │ │ • Injects custom CSS │ │ │
│ │ └──────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Performance Impact:
❌ +2-5 seconds page load (script + API calls)
❌ Framework conflicts (Angular change detection, React reconciliation)
❌ Continuous DOM manipulation (janky UX)
❌ 200-500KB bundle overhead
❌ 10-50 network requests per page
┌─────────────────────────────────────────────────────────────────────────┐
│ ✅ CONTENTFLOW SDK - Native Integration │
└─────────────────────────────────────────────────────────────────────────┘
Browser Window
┌─────────────────────────────────────────────────────────────┐
│ │
│ ┌─────────────────────────────────────────────┐ │
│ │ Your React/Angular App │ │
│ │ ┌──────────────────────────────────────┐ │ │
│ │ │ Component Tree │ │ │
│ │ │ ├─ Header │ │ │
│ │ │ ├─ <ContentComponent> (8-15KB SDK)│ │ │
│ │ │ │ ├─ Uses React hooks │ │ │
│ │ │ │ ├─ Uses Angular services │ │ │
│ │ │ │ ├─ Zustand store (1KB) │ │ │
│ │ │ │ └─ Renders from JSON (1 req) │ │ │
│ │ │ ├─ Form │ │ │
│ │ │ └─ Footer │ │ │
│ │ │ │ │ │
│ │ │ Virtual DOM / Change Detection │ │ │
│ │ │ ✅ No conflicts, native lifecycle │ │ │
│ │ └──────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ Single JSON Request (once per page): │
│ ┌─────────────────────────────────────────────┐ │
│ │ GET /data/app-page-en-US.json │ │
│ │ { │ │
│ │ "hero-title": "Welcome", │ │
│ │ "cta-button": "Get Started" │ │
│ │ } │ │
│ └─────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Performance Impact:
✅ +50-200ms page load (single JSON fetch)
✅ Zero framework conflicts (native components)
✅ No runtime DOM manipulation (uses framework lifecycle)
✅ 8-15KB bundle size (SDK only)
✅ 1-2 network requests per page
┌─────────────────────────────────────────────────────────────────────────┐
│ KEY ARCHITECTURAL DIFFERENCES │
└─────────────────────────────────────────────────────────────────────────┘
Third-Party Tools ContentFlow SDK
───────────────── ───────────────
External Script Native Library
Parallel Rendering Framework Integration
Override Lifecycle Use Framework Lifecycle
Multiple API Calls Single JSON Fetch
DOM Manipulation Virtual DOM / Change Detection
200-500KB Bundle 8-15KB Bundle
$500-2000/month $7/month self-hosted
┌─────────────────────────────────────────────────────────────────────────┐
│ THE RESULT: 10-25x FASTER PAGE LOADS │
└─────────────────────────────────────────────────────────────────────────┘
Third-Party: [████████████████████████████] 5000ms
ContentFlow: [██] 200ms
<a id="performance"></a>The Performance Story: From 5 Seconds to 200ms
ContentFlow's native framework integration cuts page load impact, network requests, and bundle size drastically compared to third-party tools like Walkme.
Let me show you the before-and-after:
| Metric |
Walkme/Third-Party |
ContentFlow |
| Page Load Impact |
+2-5 seconds |
+50-200ms |
| Runtime Updates |
Continuous DOM manipulation |
None (uses framework lifecycle) |
| Network Requests |
10-50 per page |
1-2 per page |
| Bundle Size |
200-500 KB |
8-15 KB |
| Framework Conflicts |
Frequent |
Zero |
The difference comes down to architecture. Instead of fighting the framework, we embrace it. No parallel rendering, no DOM hijacking, no lifecycle conflicts. Just clean, native integration that feels like it was always part of your app.
<a id="cost"></a>The Cost Story: $2,000/month to $7/month
Switching to a custom Azure-hosted CMS cut monthly costs from $500-2000 to $7, a 99% savings.
Yes, you read that right.
Third-party tools charge $500-2000/month. Our total Azure hosting cost? $7/month.
Here's the breakdown:
- Azure Container Apps (scale-to-zero): ~$5/month for low traffic
- Azure Blob Storage: ~$1/month for content files
- Azure CDN: ~$1/month for content delivery
- AI Translation (gpt-5.1): ~$0.001 per translation for an app
That's a 99% cost savings while delivering better performance.
<a id="features"></a>The Features: What We Built
The CMS manages four apps, five languages, AI translation, automatic image resizing, and configurable input help, all through one content system.
✅ Multi-App Content Management
Manage 4 apps from a single CMS:
- client Tax Forms (metadata-driven forms)
- Demo App (simple showcase)
- Customer Portal (client-facing)
- CMS itself
✅ Multi-Language Support
5 languages out of the box:
- English (US)
- Spanish (ES)
- French (FR)
- German (DE)
- Japanese (JP)
✅ AI-Powered Translation
Click "Translate All" and watch OpenAI's gpt-5.1 translate your entire app to all languages in seconds. Cost per batch: $0.005 (half a cent).
✅ Image Optimization
Upload an image, and the server automatically generates:
- Thumbnail (150px)
- Small (400px) for mobile
- Medium (800px) for tablet
- Large (1200px) for desktop
- WebP format for all sizes
All processed server-side with Sharp. No Azure Functions needed. Zero additional cost.
✅ Configurable Input Help
This was our latest feature—and it's a perfect example of the system's flexibility.
Product teams can now configure contextual help for form inputs:
- Enable/disable help icons per input
- Choose between info (ℹ️) or warning (⚠️) icons
- Write custom help messages
- Preview changes in real-time
The magic? Input help uses the exact same content system as text. It's just another property in the JSON file. The SDK handles it automatically.
The UI? We made those help icons state-of-the-art:
- Glowing gradient backgrounds
- Smooth animations
- Modern card popups with scrollable content
- Attention-grabbing design for warnings
<a id="ai-development"></a>The AI Development Story: Built in 2 Days
The entire CMS system was built in 2 days using Level 4 AI development, where comprehensive specs let AI write 90% of the code.
Here's where things get really interesting.
I built this entire system—SDK, CMS, server, deployment infrastructure—in 2 days (32 working hours) using what I call Level 4 AI development.
What is Level 4?
AI maturity exists on a spectrum:
- Level 0: Autocomplete (code suggestions only)
- Level 1: AI Intern (small tasks, fully reviewed)
- Level 2: Junior Partner (meaningful code, guided)
- Level 3: Code Manager (generates most code, human guides) ⭐ RECOMMENDED
- Level 4: Requirement-Driven (implements full features from specs)
Level 4 means you write comprehensive specifications, and AI implements them. I spent 4 hours writing specs, and AI wrote 90% of the code in 28 hours.
The Development Flow
Day 1, Hours 1-4: Wrote detailed specs
- Spec 001: CMS system with user stories and acceptance criteria
- Spec 002: Unified deployment to Azure Container Apps
- Spec 003: AI translation with OpenAI Batch API
- Spec 004: Configurable input help
Day 1, Hours 5-12: AI implementation
- Generated SDK core with Zustand store
- Built CMS UI with React + Tailwind
- Created server with Bun + Express
- 90% of TypeScript/React code written by AI
Day 1, Hours 13-16: Testing and validation
- Fixed bugs (Type errors, edge cases)
- Validated Azure deployment setup
Day 2, Hours 1-16: Deployment and polish
- Pulumi infrastructure as code
- Azure Container Apps setup
- Multi-app routing configuration
- Production testing
What Made Level 4 Work
✅ Clear domain: CMS and content management are well-understood problems
✅ Comprehensive specs: Every user story had acceptance criteria, edge cases, and technical requirements
✅ Modular architecture: SDK, CMS, and Server are completely independent—no tight coupling
✅ Human-defined stack: I chose React, Bun, Azure, Pulumi, OpenAI upfront
✅ Phase-by-phase validation: Tested each component before moving to the next
✅ Data models first: API contracts and storage format defined before any coding
When Level 4 Works vs. Fails
✅ Level 4 Works:
- Greenfield projects with clear scope
- Well-defined problem domains
- Modular, loosely-coupled architecture
- Specs with comprehensive edge cases
- Human architect defines contracts
❌ Level 4 Fails:
- Legacy codebases with unclear architecture
- Tight coupling between components
- Ambiguous or changing requirements
- Complex domain logic requiring expertise
- Security/compliance-critical systems
<a id="methodology"></a>The Development Methodology: Spec-Kit
A structured four-step spec process (specify, plan, tasks, implement) with human review lets AI do implementation well when specs are precise.
I followed a structured approach I call "Spec-Kit":
/speckit.specify → Create feature spec with user stories (1 hour)
↓
/speckit.plan → Generate technical implementation plan (30 min)
↓
/speckit.tasks → Break plan into 30-40 actionable tasks (30 min)
↓
/speckit.implement → AI generates code from tasks (8-12 hours)
↓
Human Review & Test → Validate each phase before continuing
Each spec lives in specs/{###-feature-name}/:
- spec.md: User stories and acceptance criteria
- plan.md: Tech stack, architecture, file structure
- tasks.md: Actionable task breakdown
- data-model.md: Entities and relationships
- contracts/: API specifications
The key insight? AI excels at implementation when you nail the specification.
<a id="results"></a>The Results: Production-Ready in 2 Days
Let me share the metrics: Development Speed ⚡ 2 days total development time (vs 2-3 weeks manual) ⚡ 32 hours working time ⚡ 90% AI-generated code ⚡ Deployed to Azure on Day 2 System Performance ✅ 4 apps managed from…
Let me share the metrics:
Development Speed
- ⚡ 2 days total development time (vs 2-3 weeks manual)
- ⚡ 32 hours working time
- ⚡ 90% AI-generated code
- ⚡ Deployed to Azure on Day 2
System Performance
- ✅ 4 apps managed from single CMS
- ✅ 11 content files across 5 languages
- ✅ Zero performance overhead (native framework integration)
- ✅ Scale-to-zero hosting (0-3 replicas based on traffic)
Cost Comparison
| Solution |
Monthly Cost |
Performance |
Development Time |
| Walkme/Pendo |
$500-2000 |
+2-5s page load |
None (but expensive) |
| Manual Build |
$5-7 Azure |
No overhead |
2-3 weeks |
| ContentFlow |
$5-7 Azure |
No overhead |
2 days |
ROI: 99% cost savings + 10x faster development
<a id="learnings"></a>The Learnings: What Worked, What Didn't
Spec-driven AI coding boosts productivity but quality fades in long sessions, so keep tasks small and context fresh.
The Good: Spec-Kit Productivity
✅ Massive productivity gains when requirements are clear
✅ AI-generated code often production-ready for well-scoped problems
✅ Structured specs reduce ambiguity drastically
✅ Great for boilerplate and repetitive patterns
The Challenges: Model Fatigue
⚠️ Model "napping": Slow, lazy responses toward the end of long sessions
⚠️ Model "yawning": Repetitive, generic output as conversation grows
⚠️ Context pollution: Quality degrades with massive conversation history
⚠️ Random shortcuts: AI sometimes takes incomplete paths and resists fixes
How to Keep Models Sharp
- Small, focused tasks: Break work into chunks. Do one thing, review, move on.
- Fresh chat windows: Start new conversations regularly to clear context
- Switch models: Rotate between different AI models when quality drops
- Don't burn it out: Treat AI like a team member—give it clear specs, check in often
<a id="tech-stack"></a>The Tech Stack: Modern & Efficient
ContentFlow CMS runs on a modern, efficient stack chosen for speed and low cost: Bun, React, Zustand, Vite, Pulumi, Azure, and OpenAI's batch API.
- Frontend: React 18 + TypeScript + Tailwind CSS
- Runtime: Bun (3x faster cold starts than Node)
- Server: Express.js on Bun
- State: Zustand (lightweight, < 1KB)
- Build: Vite (fast, modern bundler)
- Infrastructure: Pulumi (TypeScript IaC)
- Hosting: Azure Container Apps (serverless containers)
- Storage: Azure Blob Storage + CDN
- AI: OpenAI Batch API (gpt-5.1, 50% cheaper)
<a id="vision-beyond"></a>Beyond the Code: The Vision
ContentFlow CMS lets product teams edit and translate content instantly without tickets, while developers keep clean, maintenance-free integration.
ContentFlow CMS isn't just about managing content. It's about empowering product teams while respecting developer time.
For Product Teams
- Edit content without filing tickets
- See changes instantly in live preview
- Translate to multiple languages with one click
- Configure help text for complex forms
- No technical knowledge required
For Developers
- Clean, framework-native integration
- No performance overhead
- No architecture compromise
- One-time SDK setup, zero maintenance
- Freedom to focus on features, not content updates
For Companies
- 99% cost savings vs third-party tools
- Better performance than manual content management
- Faster time-to-market for content changes
- Multi-language support with AI translation
- Self-hosted, no vendor lock-in
<a id="roadmap"></a>What's Next: The Roadmap
Future work is planned in three phases: production hardening, enterprise features, and developer experience tools.
Phase 2: Production Hardening
- Real-time collaboration with conflict detection
- Content versioning and rollback
- Advanced translation workflows with human review
- Scheduled publishing
- A/B testing support
Phase 3: Enterprise Features
- Azure AD authentication
- Role-based access control (admin, editor, viewer)
- Audit logs and compliance tracking
- Multi-region deployment
- Custom CDN integration
Phase 4: Developer Experience
- CLI tool for content extraction
- VS Code extension for content IDs
- Automated accessibility checks
- Performance monitoring dashboard
- Open-source the SDK
<a id="bigger-picture"></a>The Bigger Picture: AI-Powered Development
Most teams should start with Level 3 AI development, using Level 4 only for well-scoped, familiar, boilerplate-heavy work.
Building ContentFlow taught me something profound about the future of software development.
We're entering an era where comprehensive specifications matter more than coding skill. The ability to clearly define requirements, edge cases, and acceptance criteria is becoming the bottleneck—not implementation speed.
Level 4 AI development works when:
- Problem domain is well-understood
- Requirements are comprehensive and stable
- Architecture is modular and well-defined
- Human architect defines contracts and data models
- Validation checkpoints exist at each phase
But the recommendation I give to every team? Start with Level 3.
Why Level 3 is Better for Most Projects
Level 3 (Code Manager) gives you the best of both worlds:
- Human defines "what" and "why" → AI generates "how"
- Faster iteration → No need for perfect specs upfront
- Better for exploration → Requirements emerge during development
- Real-time review → Human understands every line of code
- Easier debugging → Human maintains mental model of the system
Use Level 4 selectively for:
- Greenfield projects with crystal-clear scope
- Well-documented problem spaces
- Tight deadlines with familiar domains
- Boilerplate-heavy work (CRUD, forms, configs)
<a id="final-thoughts"></a>Final Thoughts: Build the Future You Want
ContentFlow CMS shows that native, modular, AI-assisted development lets small teams build cheap, fast tools that outperform expensive third-party software.
ContentFlow CMS represents a philosophy: technology should serve people, not constrain them.
Product teams shouldn't need to file tickets for typo fixes. Developers shouldn't waste time on content updates. Companies shouldn't pay thousands per month for slow, bloated third-party tools.
The solution? Build native. Build modular. Build with modern tools. And when appropriate, leverage AI to move 10x faster.
This project took 2 days from idea to production deployment. It costs $7/month to run. It delivers better performance than tools costing $2,000/month. It empowers product teams while respecting developer time.
And it proves that with the right architecture, the right tools, and the right AI-assisted workflow, small teams can build big solutions.
Try It Yourself
ContentFlow CMS is a production-deployed, low-cost content management library built mostly by AI in two days.
The vision is clear. The code is production-ready. The cost is negligible.
https://contentflow-app.salmonwave-4851ce16.eastus.azurecontainerapps.io/cms
If you're building web applications and struggling with content management, third-party tool bloat, or manual translation workflows—consider this approach.
Build a common library. Integrate natively. Empower your product teams. Ship faster.
And maybe, just maybe, use AI to get there 10x faster than you thought possible.
Tech Stack: React • TypeScript • Bun • Azure Container Apps • Pulumi • OpenAI Batch API
Development Time: 2 days (32 hours)
Cost: $5-7/month hosting + $0.50/month AI translation
Performance: Zero overhead vs +2-5s for third-party tools
ROI: 99% cost savings + native framework integration
Status: ✅ Production-deployed on Azure
Blog: Madhudream.dev/contentflow-cms
Demo: contentflow-demo.azurecontainerapps.io
Built with AI assistance using Level 4 development methodology. 90% of code AI-generated from comprehensive specifications. Architecture, specifications, and validation by human engineer.