RSS to Atom Feed Comparison Notes
Task 1: GUID/ID Matching ✅
Status: All match perfectly!
| Post | RSS <guid> | Atom <id> | Match |
|---|---|---|---|
| bookclub | https://alabut.com/writing/bookclub | https://alabut.com/writing/bookclub | ✅ |
| photobrainstorming | https://alabut.com/writing/photobrainstorming | https://alabut.com/writing/photobrainstorming | ✅ |
| mudfun | https://alabut.com/writing/mudfun | https://alabut.com/writing/mudfun | ✅ |
| shelterisland | https://alabut.com/writing/shelterisland | https://alabut.com/writing/shelterisland | ✅ |
Why this matters: Feed readers use these IDs to detect duplicates. Matching IDs ensure a smooth transition without duplicate posts.
How it works: The <data class="u-uid" value="..."> in your HTML maps to Atom <id> via Granary, which matches your RSS <guid>.
Task 2: Feed-Level Description ⚠️
Status: Granary limitation - confirmed not supported
RSS has:
<description>Writing by Al Abut, a product designer and startup veteran.</description>
Atom should have (but doesn’t):
<subtitle>Writing by Al Abut, a product designer and startup veteran.</subtitle>
HTML has:
<p class="p-summary">Writing by Al Abut, a product designer and startup veteran.</p>
Investigation results:
- ✅ Microformats parser (php.microformats.io) correctly sees
p-summaryin JSON output - ✅ HTML structure is correct -
p-summaryis properly nested inh-feed - ❌ Tested moving
p-summaryoutside<header>- still doesn’t work - Conclusion: This is a Granary limitation, not an HTML issue
Action items:
- File GitHub issue with Ryan (Granary creator) as feature request
- Mark as “nice to have” - not blocking for migration
Task 3: Is Feed Description Needed? 🤔
Answer: Optional, not critical
Findings:
- Feed descriptions are not required for feed readers to function
- They don’t affect duplicate detection (that’s what GUID/ID is for)
- Display varies by reader:
- Some show prominently (e.g., in feed list)
- Others only show in feed properties/settings
- Some ignore it entirely
Recommendation:
- Not blocking for migration - you can proceed without it
- Nice to have for parity with current RSS feed
- Low priority - can add later if needed
Task 4: Other Field Comparisons ✅
Feed-Level Fields:
| Field | RSS | Atom | Match | Notes |
|---|---|---|---|---|
| Title | <title>Al Abut</title> | <title>Al Abut</title> | ✅ | Perfect match |
| Link | <link>https://alabut.com/</link> | <link rel="alternate" href="https://alabut.com/" /> | ✅ | Same URL, different structure (normal) |
| ID | N/A (RSS doesn’t have feed ID) | <id>https://alabut.com/projects/microformats/mainfeed/static/</id> | ⚠️ | Atom ID is the static page URL, not site root. This is expected for Granary-generated feeds. |
| Description | <description>Writing by...</description> | Missing <subtitle> | ❌ | Known Granary limitation (see Task 2) |
| Language | <language>en-us</language> | xml:lang="en-US" | ✅ | Same language, different format (normal) |
| Updated | N/A | <updated>2025-12-05T22:33:00+00:00</updated> | N/A | Atom requires this (uses most recent entry date) |
| Author | N/A | <author><name>Al Abut</name><uri>https://alabut.com/</uri></author> | N/A | Atom has author, RSS doesn’t (normal) |
Key insight: The Atom feed ID being the static page URL is actually correct for Granary - it identifies the source HTML page. This won’t cause issues with feed readers.
⚠️ Important for production: When moving to /feeds folder, the feed <id> should point to your production feed URL (e.g., https://alabut.com/feeds/main), not the test static page.
Entry-Level Fields (Example: “bookclub” post):
| Field | RSS | Atom | Match | Notes |
|---|---|---|---|---|
| ID/GUID | <guid>https://alabut.com/writing/bookclub</guid> | <id>https://alabut.com/writing/bookclub</id> | ✅ | Perfect match (critical for duplicates) |
| Title | <title>The Non-Designer's Design Book</title> | <title>The Non-Designer's Design Book</title> | ✅ | Perfect match |
| Link | <link>https://alabut.com/writing/bookclub</link> | <link rel="alternate" href="https://alabut.com/writing/bookclub" /> | ✅ | Same URL, different structure (normal) |
| Summary/Description | <description>Revisiting a 90's classic...</description> | <summary>Revisiting a 90's classic...</summary> | ✅ | Same content, HTML entity encoding difference (normal) |
| Date Published | <pubDate>Fri, 05 Dec 2025 22:33:00 GMT</pubDate> | <published>2025-12-05T22:33:00+00:00</published> | ✅ | Same moment, different format (RSS uses RFC 822, Atom uses ISO 8601/RFC 3339) |
| Date Updated | N/A | <updated>2025-12-05T22:33:00+00:00</updated> | N/A | Atom requires both published and updated (uses same value here, which is fine) |
| Content | <content:encoded> (HTML escaped) | <content type="html"> (CDATA) | ✅ | Same HTML, different encoding (both work in readers) |
| Author | N/A | <author> (empty URI) | N/A | Atom has author element but empty (inherits from feed level) |
Date format explanation:
- RSS: Uses RFC 822 format:
Fri, 05 Dec 2025 22:33:00 GMT - Atom: Uses ISO 8601/RFC 3339 format:
2025-12-05T22:33:00+00:00 - Both represent the same moment in time (GMT = UTC = +00:00)
- Feed readers handle both formats correctly
Content encoding explanation:
- RSS: Uses HTML entity encoding (
<for<,"for", etc.) - Atom: Uses CDATA sections (
<![CDATA[...]]>) - Both preserve HTML correctly - feed readers parse both the same way
Additional Atom-Specific Elements (Not in RSS)
These Atom elements don’t exist in RSS but are not migration blockers:
| Element | Purpose | Example | Notes |
|---|---|---|---|
<link rel="alternate" href=".../static/" /> | Points to HTML source page | https://alabut.com/projects/microformats/mainfeed/static/ | Informational - shows where Granary gets the feed from. Keep HTML page private to avoid feed readers showing users a choice between HTML and Atom feeds. |
<link rel="ostatus:conversation"> | ActivityPub/OSTatus conversation thread ID | https://alabut.com/writing/bookclub | For Mastodon/compatibility - not needed for RSS migration |
<link rel="self"> (in entries) | Canonical URL of the entry | https://alabut.com/writing/bookclub | Should match entry’s alternate link (it does) |
Empty <author> in entries | Inherits from feed-level author | Empty URI, inherits name | RSS doesn’t have per-entry authors, so this is fine |
Key takeaway: These are Atom/ActivityPub enhancements. Feed readers that support Atom will use them, but they don’t affect duplicate detection or migration compatibility.
Key Concepts Learned
GUID/ID Matching
- RSS: Uses
<guid>with optionalisPermaLink="true" - Atom: Uses
<id>(always a URI) - Microformats: Uses
u-uidclass on<data>element - Critical: These must match exactly to prevent duplicates
Feed Descriptions
- RSS:
<description>at channel level - Atom:
<subtitle>at feed level - Microformats:
p-summarywithinh-feed - Status: Optional, display varies by reader
Granary Conversion
- Converts microformats2 → Atom/RSS/JSON Feed
- Uses hosted service:
https://granary.io/url?input=html&output=atom&url=... - May have limitations or quirks with certain mappings
Summary: Critical vs. Nice-to-Have
✅ Critical for Invisible Migration (All Match!)
These fields must match exactly to prevent duplicate posts in feed readers:
- Entry ID/GUID ✅ - All 4 posts match perfectly
- Entry Title ✅ - All 4 posts match perfectly
- Entry Link ✅ - All 4 posts match perfectly
- Entry Published Date ✅ - All dates match (different formats, same moment in time)
- Entry Content ✅ - HTML content matches (different encoding, same result)
Result: Your Atom feed is ready for migration from a duplicate-prevention standpoint!
⚠️ Format Differences (Normal, Not Issues)
These are expected differences between RSS and Atom formats. Feed readers handle both correctly:
- Date formats: RSS uses RFC 822, Atom uses ISO 8601 (both represent same time)
- Content encoding: RSS uses HTML entities, Atom uses CDATA (both parse the same)
- Link structure: RSS uses
<link>, Atom uses<link rel="alternate">(both work) - Author info: Atom has author elements, RSS doesn’t (optional enhancement)
📝 Nice-to-Have (Not Blocking)
These would be nice for parity but won’t cause issues if missing:
- Feed Description/Subtitle ⚠️ - Granary limitation, doesn’t affect duplicate detection
- Feed Language ✅ - Present in both (different format, same value)
- Feed Updated Date - Atom-only field (uses most recent entry date)
🎯 Migration Readiness Assessment
Status: ✅ READY FOR MIGRATION
All critical fields match. The format differences are normal and expected. Feed readers will:
- Recognize entries as the same (via matching IDs)
- Display content correctly (both encoding methods work)
- Sort by date correctly (both date formats are standard)
- Link to posts correctly (both link formats work)
Next Steps:
- ✅ Field comparison complete
- ⏭️ Test with live URL (when ready)
- ⏭️ Set up redirect from RSS to Atom
- ⏭️ Monitor for any issues after switch
Production Migration Checklist
When moving from test (/projects/microformats/mainfeed/static/) to production (/feeds/):
Feed Structure
- Create
/feeds/main.htmlfor production feed (HTML source with microformats) - Create
/feeds/test.htmlfor safe experimentation - Atom feed will be generated by Granary from these HTML files
- Ensure feed
<id>points to production feed URL (e.g.,https://alabut.com/feeds/main) - Ensure feed
<link rel="alternate">points to site root (https://alabut.com/)
Note: Keep HTML pages private/test-only to avoid feed readers auto-discovering them and showing users a choice between HTML and Atom feeds.
Feed ID Considerations
Current (test): <id>https://alabut.com/projects/microformats/mainfeed/static/</id>
Production should be: <id>https://alabut.com/feeds/main</id> (or your chosen feed URL)
Why this matters: The feed ID is a stable identifier. If it changes between test and production, some feed readers might see it as a “new” feed. However, since you’re redirecting from the old RSS URL, this should be fine - readers will follow the redirect and get the new feed ID.
Redirect Strategy
- Set up 307 (Temporary) or 308 (Permanent) redirect from
https://alabut.com/rss.xmlto new Atom feed - Test redirect with multiple feed readers
- Monitor for any duplicate post issues after switch
Static vs Dynamic Template
Recommended approach: Use static HTML first, convert to Astro template later
Why static first:
- ✅ Fewer moving parts during critical migration phase
- ✅ Easier to verify field matching with known static file
- ✅ Less risk - if issues arise, you know it’s not template logic
- ✅ Can validate entire migration before adding complexity
When to convert to Astro:
- After migration is validated and stable
- After redirect is working correctly
- When ready to automate post generation (like
/writingindex)
Alternative: Convert to Astro now if you’re confident, but adds complexity during migration validation.