Developer Portal Homepage Architecture โ
The homepage redesign (issue #639) replaces developers.sap.com/ with a new top-level developer gateway. This document covers the platform engineering view: data model, services, build-time data feeds, runtime endpoints, and operational handles.
Spec: docs/superpowers/specs/2026-06-27-639-developer-homepage-design.mdPlan: docs/superpowers/plans/2026-06-27-639-developer-homepage.md
Components โ
| Layer | Component | Source |
|---|---|---|
| Data model | HomepageShelves entity + admin Fiori app | db/homepage.cds, app/admin-annotations.cds |
| Data model | LegacyRedirects entity + admin Fiori app | db/homepage.cds, app/admin-annotations.cds |
| Data model | HomepageConfig singleton + admin Fiori app | db/homepage.cds, app/admin-annotations.cds |
| Service | HomepageService (@path: /api/homepage) | srv/homepage-service.cds, srv/homepage-service.js |
| Build feed | HomepageShelvesEndpoint (GET /build/homepage-shelves) | srv/developer-service.js |
| Fetcher | YouTube fetcher (srv/lib/youtube-fetcher.js) | Calls YouTube Data API v3 |
| Fetcher | RSS fetcher (srv/lib/homepage-rss-fetcher.js) | SAP Community blogs + SAP News RSS |
| Merger | Events merger (srv/lib/homepage-events-merger.js) | Merges DB events + events calendar |
| Resolver | Legacy-redirects resolver (srv/lib/legacy-redirects-resolver.js) | Loads LegacyRedirects from DB, refreshes hourly |
| Approuter | Loader + hit counter (approuter/lib/) | Loads redirectsActive at startup, records POST /api/homepage/recordRedirectHits |
| Cron | Link-health job (srv/jobs/homepage-link-health.js) | Nightly 04:00; updates HomepageShelves.linkStatus |
| Cron | Reshuffle video rotation (srv/jobs/reshuffle-video-rotation.js) | Every 4h @ :19; ranks ext.Videos by view velocity into HomepageVideoRotation |
Page Anatomy โ
Seven rows top-to-bottom on the homepage. Each verb also has a dedicated sub-page at /<verb>/:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Row 1 ยท Hero โ
โ One short sentence. No CTAs, no search bar, no campaign slogan. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Row 2 ยท Verb spine โ
โ Six tiles: Learn ยท Build ยท Integrate ยท Operate ยท AI ยท Connect. โ
โ Each tile previews the Start Here shelf + links to /<verb>/. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Row 3 ยท Events band โ
โ 6 upcoming events, auto-pulled from CommunityEvents, region- โ
โ filtered per user preference. Runtime: /homepage/events (60s โ
โ per-key cache; flag: HomepageConfig.eventsBandAutoPullEnabled). โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Row 4 ยท SAPDevs video band โ
โ LEFT โ Weekly Developer News. RIGHT โ up to 6 tiles: 3 newest โ
โ (anchors) + 3 popular (rotation, every 4h). #1031 โ
โ Runtime: /api/homepage/videos (15-min cache; YouTube Data API v3). โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Row 5 ยท Featured missions carousel (topic-based, 8 slides ร 4 missions) โ
โ KG-driven topic slides with editorial override. SSR baseline: โ
โ hugo/data/featured_topics.json. Runtime: /homepage/featuredTopics.โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Row 6 ยท Community lane โ
โ Columns: Developer Advocates ยท Community blogs ยท SAP News. โ
โ Runtime: /api/advocates + /api/homepage/communityBlogs + โ
โ /api/homepage/news (30-min cache for feeds). โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Row 7 ยท Comprehensive directory footer โ
โ 6 columns (one per verb). All 50+ destinations grouped by verb. โ
โ Build-time: hugo/data/homepage_shelves.json. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโVerb Sub-Page Contract โ
Each verb sub-page at /<verb>/ renders all HomepageShelves entries for that verb, organized into four named shelves:
| Shelf key | Purpose |
|---|---|
START_HERE | 1-3 marquee entry points; admin-curated |
REFERENCE | Canonical docs and APIs; admin-curated |
TOOLS | IDEs, SDKs, GitHub org links; admin-curated |
KEEP_CURRENT | Videos, community, news for this verb; mixed curated + live |
Three verb sub-pages carry an extra section beyond the four shelves:
| Sub-page | Extra section |
|---|---|
/learn/ | Curated learning paths (missions / learning journeys) |
/operate/ | BTP service catalog teaser (Discovery Center feed) |
/connect/ | Events calendar (full upcoming event list) |
Data Flow โ
| Row | Data source | Freshness mechanism |
|---|---|---|
| Row 1 hero | Static (Hugo front matter) | Rebuild only |
| Row 2 verb spine | hugo/data/homepage_shelves.json (baked from GET /build/homepage-shelves) | Rebuild on admin HomepageShelves save (debounced 60s dispatch) |
| Row 3 events | GET /homepage/events?region=<X>&includeVirtual=<b> โ 60s per-key cache; sourced from CommunityEvents when HomepageConfig.eventsBandAutoPullEnabled=true else legacy Events entity | 60s per-key server-side cache |
| Row 4 videos | GET /api/homepage/videos | 15-min server-side cache; depends on YOUTUBE_API_KEY |
| Row 5 featured carousel | hugo/data/featured_topics.json (baked by scripts/fetch-featured-topics.ts) + runtime /homepage/featuredTopics | Nightly job at 04:13 UTC (kg-featured-topics-job) + editorial-save debounced rebuild (60s) |
| Row 6 community | /api/advocates + GET /api/homepage/communityBlogs + GET /api/homepage/news | Advocates: 60s + SWR; RSS feeds: 30-min cache |
| Row 7 directory footer | hugo/data/homepage_shelves.json | Same as Row 2 |
homepage_shelves.json is generated during build:all by fetching GET /build/homepage-shelves from the CAP backend (same pattern as /build/catalog for missions). This bakes shelf content into the Hugo build so the directory footer and verb-spine previews work without a runtime API call.
URL Contract โ
New URLs (spec ยง9.1) โ
| URL | Serves |
|---|---|
/ | New developer-portal homepage |
/learn/ | Learn verb sub-page |
/build/ | Build verb sub-page |
/integrate/ | Integrate verb sub-page |
/operate/ | Operate verb sub-page |
/ai/ | Extend-with-AI verb sub-page |
/connect/ | Connect verb sub-page |
/tutorial-navigator/ | Relocated tutorial navigator (was /) |
Legacy Redirects (spec ยง9.3) โ
Managed via the LegacyRedirects CDS entity. The approuter middleware (approuter/server.js) loads the redirect map at startup from GET /api/homepage/redirectsActive and refreshes hourly. Redirect hits are written back via POST /api/homepage/recordRedirectHits (idempotent batch, approuter-internal).
| Legacy URL | Target | Type |
|---|---|---|
/tutorial-navigator.html | /tutorial-navigator/ | 301 named |
/index.html | / | 301 named |
/topics/<tag>.html | /tags/<tag>/ | 301 pattern |
/mission.html?id=<id> | /missions/<slug>/ | 301 pattern |
/group.html?id=<id> | /groups/<slug>/ | 301 pattern |
/<any>.html | /<any>/ (if exists) | 301 catch-all |
Admin Operations โ
Add shelf entry: Open /admin-ui/#homepage, Shelves tab, press Create. Set verb, shelf, title, url, and optionally description, badge, sortOrder. Save triggers a rebuild dispatch (60s debounce) so the change appears in hugo/data/homepage_shelves.json within ~2 minutes.
Add legacy redirect: Open /admin-ui/#homepage, Redirects tab, press Create. Set fromPath, toPath, statusCode (301 default), and optionally isPattern. The approuter middleware picks up changes on its next hourly refresh โ no redeploy needed.
Update YouTube playlist ID: Open /admin-ui/#homepage, Config tab, edit developerNewsPlaylistId (the YouTube playlist ID for the featured Developer News series). The 15-minute video cache expires automatically; no restart needed.
Nightly link-health: The homepage-link-health cron job (04:00 daily) sends HEAD requests to every active HomepageShelves.url and writes linkStatus (OK | SLOW | BROKEN) + lastChecked back to the entity. Broken links surface as a red dot on the Shelves tab in the admin UI. Threshold for SLOW is 1500ms (default); timeout is 5000ms per URL; concurrency is 4 with 200ms between requests.
Explainer popovers โ
Issue #759 adds progressive-disclosure explainers to the homepage verb spine, the directory footer, and the verb sub-page link cards. The data model (VerbDefinitions, ShelfDefinitions, plus three new fields on HomepageShelves), build feeds, AI-generation actions, Vue islands, and admin workflow are documented separately to keep this file focused.
See Homepage explainer popovers for:
- Data model (3 entities + the
AuthoringStatuslifecycle) - New build feeds (
/build/verb-definitions,/build/shelf-definitions) - AI-generation actions on
AdminServiceand theAICORE_EXPLAINER_GENERATOR_DISABLEDkill switch - Vue islands (
verb-flip-tile,link-explainer-popover) and their Hugo attach points - Admin UI surfaces under
/admin-ui/#verb-definitions,/admin-ui/#shelf-definitions, and the Explainer facet on the Homepage Shelves Object Page - Authoring workflow for new BTP environments
Featured missions carousel (#1032) โ
Row 5 of the homepage was previously a static tutorials-catalog teaser. Issue #1032 replaces it with a KG-driven topic carousel: up to 8 slides, each showing up to 4 mission cards for a concept ranked by the nightly PageRank job.
Data flow: The nightly kg-featured-topics-job (04:13 UTC) calls recomputeSnapshot, which joins ConceptRank ร HomepageFeaturedTopics editorial rows ร KgCommunity diversity filter, then materialises the result into FeaturedTopicsSnapshot. scripts/fetch-featured-topics.ts reads the snapshot via GET /build/featured-topics and writes hugo/data/featured_topics.json for SSR. At runtime, the Vue island (hugo-apps/src/featured-topics-carousel/) re-hydrates via GET /homepage/featuredTopics() with weak ETag / 304 caching.
Admin surface: /admin-ui/#featured-topics โ FeaturedTopics CRUD + manual recomputeFeaturedTopics action (SuperAdmin-gated).
Kill switch: revert the Row 5 Hugo partial include (hugo/layouts/partials/homepage/featured-topics-carousel.html) to restore the previous teaser โ no DB migration needed.
Spec: docs/superpowers/specs/2026-07-06-1032-featured-missions-carousel-design.mdPlan: docs/superpowers/plans/2026-07-06-1032-featured-missions-carousel.md
SAP News (developer-relevance filter โ #1034) โ
The homepage /homepage/news handler serves items from the NewsItems HANA table when the two-layer kill switch is on. srv/jobs/fetch-news-job.js runs hourly (:37) against news.sap.com/feed/; each item is classified by srv/lib/relevance-classifier.js (embedding-first via RelevanceSeedExemplars, LLM fallback for the mid-band, keyword rules on any error).
Admins triage at /admin-ui/#content-moderation โ approve, reject, clear override, or reclassify a single item. Admin verdicts win over AI at read time. Homepage items are capped at 2, aged out after 14 days, English-only.
Kill switches (either off โ legacy RSS pass-through):
- Env
HOMEPAGE_NEWS_RELEVANCE_ENABLED(default: unset; only the literal stringfalsedisables โ any other value, or absence, treats as enabled). HomepageConfig.newsRelevanceEnabled(defaultfalse).
Community Blog Posts (#1033) mirrors this pattern using the same ContentModerationService + RelevanceSeedExemplars shared seed set.
Personalization for signed-in users โ
Issue #763 adds per-user reordering + filtering + a "For you" row. See homepage-personalization.md for:
- Endpoint contract + ETag/304 +
X-Personalization: 1marker - Persona-tag admin workflow
- BroadcastChannel live re-render +
?default=1bypass - Kill switch (
HomepageConfig.personalizationEnabled)
Failure Modes โ
| Failure | Behaviour |
|---|---|
| YouTube API 403 or timeout | Video band renders a static link card to @sapdevs YouTube channel. No crash. |
| Community blogs / SAP News RSS 4xx or network error | Empty column with a direct link to community.sap.com / news.sap.com. No crash. |
| Events DB unavailable | Events band renders empty. No crash. |
HomepageShelves link-health BROKEN | Red indicator on admin Shelves tab only. No user-facing impact (links still render; admins decide whether to disable). |
| Approuter โ srv unavailable at startup | Legacy-redirects resolver skips load and logs a warning; middleware retries on the next request. No boot crash. |
HomepageConfig missing | Admin auto-init handler creates the singleton on first READ with safe defaults (videoBandEnabled: true, eventsBandEnabled: true, communityLaneEnabled: true). Consistent with the pattern used by ChatSettings, DisplaySettings, etc. |
YOUTUBE_API_KEY not set | youtube-fetcher.js returns an empty array; video band degrades gracefully to the static link card. |
| Reshuffle cron throws | HomepageVideoRotation untouched (single-tx ROLLBACK). Stale rotation continues to serve. |
HomepageVideoRotation empty (fresh deploy) | Response returns anchors only; client renders 3 tiles until first cron pass. |
Statistics fetch fails in fetch-videos-job | Snippet upsert already succeeded; view/like counts stay stale. Rotation deprioritises null-viewCount rows to bottom. |
Site Integration โ
The new pages live inside the same Hugo site shell as the rest of developers.sap.com. Header (<ui5-shellbar>), footer, Joule panel, alerts popover, command palette, cookies banner, and theme switcher all render unchanged via hugo/layouts/_default/baseof.html.
Page-kind dispatch drives per-page Joule starters + behaviour. baseof.html writes data-page-kind="..." on <html>:
| Page | data-page-kind |
|---|---|
/ | homepage |
/learn/ | verb-learn |
/build/ | verb-build |
/integrate/ | verb-integrate |
/operate/ | verb-operate |
/ai/ | verb-ai |
/connect/ | verb-connect |
/tutorial-navigator/ | tutorial-navigator |
/tutorials/<slug>/ | tutorial (unchanged) |
Joule starters per page-kind live in hugo/layouts/partials/joule-starters.html. The new homepage's set is concierge-shaped โ "where can I find โฆ?" prompts that nudge users toward the right destination in the SAP developer landscape. Per-verb sets nudge deeper into each lane.
Navigate popover (hugo/layouts/partials/header.html) was reorganised on the cutover to surface the new lanes: Home โ 6 verb pages โ Tutorial navigator โ existing sub-pages (App Space, Event Display, Devtoberfest, Developer Advocates) โ conditional Me / Admin UI.
Deferred enhancement โ Joule chat handler routes to catalog โ
The current implementation provides discovery-shaped starter prompts but the underlying Joule chat handler (srv/chat-service.js + srv/lib/chat-orchestrator.js) is the same tutorial-RAG handler used elsewhere on the site. It can answer "where can I find SAP BTP AI best practices?" reasonably because BTP-AI Best Practices is in the tutorial corpus, but it has no first-class knowledge of the HomepageShelves catalog.
A future enhancement would teach the chat orchestrator to call /api/homepage/shelves?verb=<v> and /api/homepage/redirectsActive, treating the catalog rows as first-class retrieval sources alongside tutorial content. On a homepage or verb-<key> page-kind, the handler would prioritise catalog-shelf citations over tutorial-step citations and link out to the appropriate destination URL.
That work is out of scope for issue #639 and lives as a future follow-up. The infrastructure (catalog data + endpoint + admin-curated content) is already in place.
CodeJams auto-pull (#1030) โ
Issue #1030 rewrites Row 3 (events band) to pull from CommunityEvents automatically and adds per-user region preference.
Spec: docs/superpowers/specs/2026-07-07-1030-homepage-codejams-autopull-design.mdPlan: docs/superpowers/plans/2026-07-07-1030-homepage-codejams-autopull.md
Video band rotation (#1031) โ
Row 4's right stack expands from 3 to 6 tiles (configurable via HomepageConfig.videoBandAnchorCount + videoBandRotationCount). Anchors always show the most recently published videos; the rotation slot set is materialised into HomepageVideoRotation every 4h by srv/jobs/reshuffle-video-rotation.js, ranked by view velocity (views per day since publishedAt) over the trailing videoBandRotationWindowDays (default 90).
Admin surfaces:
/admin-ui/#videosโ toggleexcludeFromHomepageper video; manualrecomputeHomepageVideoRotationaction (SuperAdmin-gated)./admin-ui/#video-rotationโ read-only view of the current rotation./admin-ui/#homepageConfigโ tuning knobs.
Kill switches:
videoBandRotationCount = 0โ anchor-only (existing 3-tile behaviour). Zero deploy.videoBandEnabled = falseโ whole band disabled (unchanged from before).