Table of contents:
The model of decoupling the generation of a site’s assets from the time they are requested brings a variety of advantages, but also introduces challenges when sites become very large.
As the Jamstack ecosystem has matured, tools and services have emerged bringing ways to automate deploys and provide productive workflows, but the practical ceiling for Jamstack sites often involves maintaining practical site generation (build) times when the number of pages being created in each build becomes very large.
Various strategies have risen in an attempt to satisfy this need such as incremental builds, and incremental static regeneration (ISR) + the use of the stale while revalidate (SWR) pattern.
Each of these approaches is either difficult for developers to implement and reason about, or falls short of upholding core principles of the Jamstack thus compromising some of the key benefits of its architecture.
What are we trying to achieve?
Distributed Persistent Rendering (DPR)
With DPR, the generation, or rendering of assets is distributed between build-time and request-time.
With DPR, the responsibility of rendering assets is distributed between the build infrastructure, and serverless functions.
With DPR, the cache of assets which form the site’s deploy can grow progressively and persists over time as requests are made to more URLs which were not previously rendered.
Logically, DPR creates the same result as a Jamstack build - it renders assets and populates them into a CDN or hosting infrastructure. Rather than the rendering of every asset taking place at build time, the rendering of some assets can be deferred from build time and instead take place on demand when each is first requested. These assets then join those previously rendered during the build process or by other on demand requests, in the CDN, logically contained within the same atomic deploy.
In this way DPR would provide the means of rendering some assets at build time, and others, later via serverless functions, as a result of the first request to their URL.
Like the assets generated during a build, those rendered by DPR at request time would remain in the CDN cache until invalidated by the successful completion of a new deploy. This would allow developers to consider the assets rendered during a deploy, and those rendered on demand from requests to DPR functions contained in that deploy as all belonging to the same logical atomic deploy.
Image: A logical overview of the areas of responsibility for a given deploy with DPR
Example scenarios utilizing DPR:
Critical and archived pages
Consider a news or publication site which may have hundreds of thousand or millions of unique content pages on unique URLs. Typically, over time, the frequency of updates to specific news story pages diminishes, as does the frequency of requests to them.
DPR would allow the site developers to focus on core content pages in their build, regularly generating those pages as a result of content updates, and as a result of feature and design iterations. The long tail of historic pages might be omitted from the core build such that the build times remain fast and manageable. Historic pages would then get added to the latest deploy output only if they are requested via their public URL. Once rendered on demand, they would remain available until invalidated by a subsequent deploy. Thereafter they would be repopulated by their next first request.
User generated content pages
DPR could also be used to populate pages created as a result of content contributed by users. Consider a site which invites users to submit content via a form which would later be presented on a unique page per contribution.
Instead of regenerating the entire site upon each user submission, only the new page which should exist as a result of the user’s contribution would need to be generated. This rendering could take place on-demand when the URL for the new page is first requested and added to the overall deploy cache for this version of the site.
Updates to the site’s design or functionality would trigger a new deploy as usual, but the build times would no longer be coupled to the volume of user generated content as the rendering of these pages would be deferred until they were first requested.
Points of difference
Key differences between DPR and other strategies for achieving these goals.
Incremental Builds build only certain parts of your site when there are changes. DPR would instead rebuild the whole site, with the exception of the content you want to be rendered on demand.
Incremental static regeneration (which is based on stale while revalidate), similar to DPR, generates only the pages defined, and then renders the new page when a user navigates to that page. That being said, SWR relies on users seeing stale content first. Whether it is a fallback page or a previous version of the page, it is not a consistent experience for each user. The first user to a new page will see stale content, and the second user (and beyond) will see the newest content.
Exclusions and omissions
This RFC does not include descriptions of internal CDN caching strategies or other implementation details. This is a deliberate omission so that we can focus this discussion on the logical model for DPR and the mental model for building sites and applications with it.
Deeper CDN implementation details are out of scope for this discussion and should be possible to be kept opaque to the developers implementing sites with DPR just as they should for existing Jamstack sites.
How would on-demand builders work with Jamstack SPA sites?
I'm the maintainer of Docusaurus (quite similar to Gatsby, using Webpack and React for SSG) and wonder how easy it can be to generate that lambda function to generate pages lazily. That seems easier with non-SPA jamstack tools or Next.js and SSR-based tools that already generate lambdas.
Has anyone made a POC with Gatsby or a similar tool?