Skip to content

Blog


Building a Marketing Engineering Platform using Next.js, Cloudflare, and Contentful

February 22, 2022

|
Harry Dehal

Harry Dehal

Josh Santomieri

Josh Santomieri

Finding the right balance between engineering and marketing has always been a challenge. In engineering projects, one focuses primarily on the user experience (UX), and subsequently, the developer experience (DX) that it takes to get there. But when marketing is added to the equation, the experience of marketing stakeholders, including the content team and designers, needs to be factored in.

Historically, marketing at DoorDash has been divided by DoorDash’s three-sided marketplace with Consumer, Dasher (delivery drivers), and Merchant, all with their own websites, content, design, and external agency support. As the number of external marketing websites grew, the quality, consistency, and manageability of these external websites began to suffer.

As the first engineering hires within the Growth Marketing group at DoorDash, we started up with a thesis that speed, performance, unification, and bringing engineering best practices into marketing are important principles. We spent 2021 building a scalable, highly performant platform that would allow marketers to standardize and scale up internationally – without the need for engineers or designers after initial setup – all on one multi-tenant team-agnostic, unified platform.

Simplifying, then adding lightness (legacy CMS woes)

Marketing at DoorDash in September 2020 was a simpler time. The company had a few months before its initial public offering (IPO) and COVID-19 had completely changed the restaurant landscape for the worse. The initial charter was to build a web team and manage the merchant acquisition website (get.doordash.com), responsible for more than 50% of restaurant sign-ups for the company.

The legacy CMS was being pushed to its limits. Issues included:

  • Minimal templatization and reusability – components, navs, headers, and footers were out of sync and inconsistent from years of manual edits
  • No brand or UI/UX consistency across websites
  • 99% of content was not translated
  • Analytics were hit and miss; duplicative tags and console errors
  • Events and queries were not documented
  • Pages were cloned and manually hardwired to Salesforce campaigns

In addition to the merchant acquisition website, there were nearly two dozen other websites in a similar state within DoorDash. It came as no surprise that teams had formed a habit of using external agencies to launch siloed microsites on a dizzying array of external platforms. 

All these teams were trying to do far too much, too manually, and too slowly. We needed to switch gears and build a platform that would put in UI/UX guardrails and promote engineering best practices, helping the company scale its marketing websites globally.

Stay Informed with Weekly Updates

Subscribe to our Engineering blog to get regular updates on all the coolest projects our team is working on

Building a Ferrari (or static site generation with Next.js)

DoorDash is a React and TypeScript shop, with a Design Language System and shared UI component library called Prism, managed by our design team. We’d want to choose a stack that would be consistent, manageable, and aligned with the values of other engineering teams – remember, the goal of this project is unification, not division.

We started with the merchant acquisition website and were determined to build a “Ferrari” that would be optimized for speed and performance – and we’d need to build a platform that we could reuse and leverage for other orphan websites down the line.

With the Google algorithm updates (Web Vitals) looming on the horizon, there would be a renewed focus on performance, page speed, and user experience. Static site generation was the obvious path forward, and our options quickly narrowed down to Gatsby and Next.js.

We staged proofs-of-concept for both frameworks and were impressed with the speed of static site generation.  Still, the developer experience of Next.js felt far more like a natural extension of React, and it was love at first test drive, for several reasons:

  • Production-ready (we’re in good company with other enterprise users)
  • Large community support
  • TypeScript support

We would dub this project internally as “Project Ferrari.” Next, we’d need to find a content management system (CMS) that could keep up.

Putting the stack aside, we knew we’d need a headless CMS for marketers and copywriters to create and manage content without the help of an engineer and designer – they couldn’t commit directly to a GitHub repo, could they?

We pared the list of candidates down to a handful of content management systems, with only a few meeting our stringent security and compliance requirements – even fewer felt as well-rounded, documented, and tenured as Contentful with the following features that made the decision a simple one:

  • Translation and localization support (i18n and l10n)
  • SAML SSO integration for centrally managed account permissions
  • Security certifications (ISO 27001 and/or SOC2 Type II)

Once we defined the tech stack and content management system, we needed to align on the technical project goals.

Defining technical project goals

We needed to ensure that the platform we were building would be easily reusable and scalable beyond just the merchant acquisition website that we were using as our initial pilot. Thinking about our long-term goals for the project, we knew we needed to:

  • Create a multi-tenant, team-agnostic website platform
  • Centralize content management across all core marketing web properties
  • Focus on templatization and sharing components for a cohesive UI/UX
  • Standardize and document analytics and event tracking
  • Optimize for performance – Google Web Vitals, page speed, and SEO
  • Live and breath accessibility (a11y), internationalization (i18n), and localization (l10n)

Internationalization (i18n) as inclusiveness

When we started this project, DoorDash was officially live in the United States, Canada, and Australia, and we pushed marketers to start thinking about internationalization as inclusiveness – all campaigns launched in the United States needed to include both English and Spanish. At the same time, Canada required both English and French (with strict enforcement in Québec).

In 2021, we also launched in Japan and Germany – our language requirements on the web were growing exponentially, far faster than the team could manage manually on the old platform.

Fortunately, Contentful has built-in connectors for popular services such as Memsource or Smartling, making it easy for marketers to manage translations and localization directly in the CMS (see Figure 1 below). In the code, we went with an extremely lightweight package (<300 bytes, including dependencies) called Rosetta (Next.js code example here).

Figure 1: The two-way flow of translation strings between Contentful and our translation service

Setting up CI/CD and hosting

At this point, we were trying to figure out where to host our Next.js project. Suppose we were to leverage existing infrastructure at DoorDash. In that case, we’d need to dockerize Next.js, integrate with existing Jenkins CI/CD pipelines, and find a way to get it hosted on AWS – and that would require hiring additional DevOps engineers to help set up and manage this infrastructure – a task and scope far larger than our small Marketing Engineering team could handle.

There had to be an easier way. We simply wanted to commit code to a repo, trigger a build (automatically with webhooks), and deploy a website in minutes. 

We also wanted our team to spend more hands-on time working on marketing projects instead of getting in the weeds managing CI/CD infrastructure. More importantly, there was an urgency to see our work directly impact restaurants and merchants faster (we were now in the pandemic winter surge of 2021 and restaurants were closing their doors again).

Deploying to the edge

By chance, we stumbled upon a discussion on Hacker News on a Cloudflare blog post called, “Introducing Cloudflare Pages: the best way to build JAMstack websites.” In the comments, there were engaging replies by Cloudflare product managers and engineers. After some sleuthing, we reached out asking if they could squeeze us in for early beta access to Cloudflare Pages.

Cloudflare Pages takes care of the build and deploy process with a simple “git push” and we already know that static pages generated by Next.js are fast… But what if we could build and deploy static pages directly to the edge of Cloudflare’s global network (see Figure 2 below)? 

Figure 2: Deploying static content to Cloudflare’s edge network helps us reach our visitors globally at breakneck speeds.

Exactly 18 minutes after we reached out to Cloudflare, we received a reply from a product manager with a green light and test accounts! And that’s been our experience and relationship with Cloudflare ever since – fast-paced, energetic, collaborative, and overwhelmingly positive. The road ahead was finally clear.

Measuring performance and Web Vitals

February through April were wide open throttle (WOT) and a complete blur. Our team took charge of the UI/UX design and mockups ourselves, leveraging Prism. But soon, we realized that our small team wouldn’t be able to build and migrate thousands of marketing pages by summer.

Understandably, our stakeholders wanted to see proof of what kind of lap times our so-called Ferrari could actually put down (in production) before they committed to the entire migration and encouraged us to run an A/B test.

To test and track our real-world performance, we would need to standardize on analytics events and measure user experience and signup conversion on the website. We use the “track” API call from Segment.io with the following payload from the “web-vitals” project:

const WebVitalNameMap = {
 ['FCP']: SegmentEventName.WEB_VITALS_FCP,
 ['LCP']: SegmentEventName.WEB_VITALS_LCP,
 ['CLS']: SegmentEventName.WEB_VITALS_CLS,
 ['FID']: SegmentEventName.WEB_VITALS_FID,
 ['TTFB']: SegmentEventName.WEB_VITALS_TTFB,
}
 
export function trackWebVital(name: string, id: string, value: number): void {
 const segment = useSegment()
 const eventName = WebVitalNameMap[name]
 
 if (eventName) {
   segment.track(eventName, {
     version: SegmentEventVersions.NewMxWebsite,
     page_name: window?.location?.pathname || '',
     page_url: window?.location?.href || '',
     id,
     value,
   })
 } else {
   console.warn(`Web Vital Name Map Missing: ${name}`)
 }
}

We then pipe event data into Amplitude (see Figure 3), where we can easily create charts to track performance. Note the red, downward trend is a good thing here (we’re adding lightness and shaving off lap times from our Core Web Vitals metrics with each and every code release):

Figure 3: We use Amplitude fed from Segment.io data to monitor our Web Vitals (LCP, FID, and CLS) for each and every marketing deployment

A/B testing with Cloudflare Workers

Now that all the pieces were in place, it was time to release the first version of our new website in a controlled rollout. We created an A/B test for our highest-traffic page and the entry-point for merchant onboarding on get.doordash.com
But how could we effectively split traffic 50/50 between our old CMS against a new CMS and platform on a completely different stack, while seamlessly using the same URL structure and subdomain? Cloudflare Workers and the DoorDash Traffic Engineering Team to the rescue:

// https://developers.cloudflare.com/workers/examples/ab-testing
 
function handleRequest(request) {
 const NAME = "experiment-0"
 
 // The Responses below are placeholders. You can set up a custom path for each test (e.g. /control/somepath ).
 const TEST_RESPONSE = new Response("Test group") // e.g. await fetch("/test/sompath", request)
 const CONTROL_RESPONSE = new Response("Control group") // e.g. await fetch("/control/sompath", request)
 
 // Determine which group this requester is in.
 const cookie = request.headers.get("cookie")
 if (cookie && cookie.includes(`${NAME}=control`)) {
   return CONTROL_RESPONSE
 }
 else if (cookie && cookie.includes(`${NAME}=test`)) {
   return TEST_RESPONSE
 }
 else {
   // If there is no cookie, this is a new client. Choose a group and set the cookie.
   const group = Math.random() < 0.5 ? "test" : "control" // 50/50 split
   const response = group === "control" ? CONTROL_RESPONSE : TEST_RESPONSE
   response.headers.append("Set-Cookie", `${NAME}=${group}; path=/`)
 
   return response
 }
}
 
addEventListener("fetch", event => {
 event.respondWith(handleRequest(event.request))
})

In a matter of weeks, the DoorDash Analytics team waved the checkered flag and confirmed that our new platform resulted in a +12.12% increase in lead conversion and a +21.33% increase in the closed won conversion rate (at 99% statistical significance).

Beyond signup conversion, we improved time spent on site by +95.5% and lifted Web Vitals overall, decreasing LCP by 30.5% (see Figure 4). The Largest Contentful Paint metric, according to Google, “is an important, user-centric metric for measuring perceived load speed because it marks the point in the page load timeline when the page’s main content has likely loaded…”

Figure 4: Migrating to our new platform pushed Web Vitals into the green for the first time

The results of the A/B test far exceeded expectations and were a much-needed validation of months of hard work. Now that we had proven ourselves and our platform, we would move on to the long, arduous process of creating, testing, and deploying a library of reusable UI components, documentation, and training the marketing team on how to use Contentful to build pages on the fly – without the need for an engineer or designer to create custom code or mockups for each and every project.

Spinning up a new website is now only a matter of minutes – simply add a project to the monorepo, provision Contentful accounts, setup a Cloudflare Worker, and you're off to the races! Did we somehow manage to capture the bleeding-edge performance of a Ferrari with the long-term vision, scalability, and reliability of The Toyota Way?

Scaling up the platform

Given the success of the initial website, it became clear that the overarching goal of the Marketing Engineering team would be to extend and manage our multi-tenant, team-agnostic platform that could help all our separate marketing teams unify their web presences and scale-up.

As the platform grew (see Figure 5 below), we helped the business expand beyond restaurants into other verticals (such as grocery, alcohol, convenience, flowers, etc.) and launch into new international markets (Japan and Germany) – effectively stress-testing each strategic pillar that we build into every component in our codebase:

  • Analytics and reporting – using Segment.io for event tracking and piping the data into Amplitude so marketers and engineers alike can easily create meaningful charts; we also use Google Analytics, Google Tag Manager, and a third-party tool for GDPR compliance
  • Forms and attribution integrating with an ESP and Salesforce CRM both for email and campaign-specific routing and attribution
  • Performance and Web Vitals – optimizing the codebase, reducing third-party scripts and pixels, measuring Web Vitals, and developing for mobile-first
  • Internationalization (i18n) and localization (l10n) – integrating our translation service directly in Contentful
  • Accessibility (a11y) – using Lighthouse Accessibility Scoring (part of Web Vitals), Web Accessibility Evaluation Tool (WAVE), color contrast, keyboard navigation, etc.
  • A/B testing and CRO – leveraging Optimizely to help quickly launch A/B and multi-arm bandit tests
Figure 5: Overview of the Marketing Engineering platform and integrations, including Cloudflare Pages and Contentful

We’re proud to have been hands-on across the company, having an outsized impact on improving onboarding processes, pricing transparency, privacy and GDPR, reliability of our APIs, and doubling down on accessibility, internationalization, and localization for all.

What’s next for Marketing Engineering?

For 2022, we’re starting to share the platform and components for multi-tenancy by empowering other teams like Dasher, Caviar, Chowbotics (and more) to move onto our infrastructure.

Our small but mighty team is also growing (there are three of us engineers in marketing now, with the support of two Salesforce analysts and a data science and testing strategist). The need for Marketing Engineering to help the company scale internationally and for businesses beyond restaurants is now stronger than ever. We haven’t slowed our momentum or taken our foot off the gas pedal since.

About the Authors

Related Jobs

Job ID: 2915998
Location
Sao Paulo, Brazil
Department
Engineering
Location
Sunnyvale, CA
Department
Engineering
Location
Pune, India
Department
Engineering
Location
São Paulo, Brazil
Department
Engineering
Location
San Francisco, CA; Tempe, AZ
Department
Engineering