By Ken Sheedlo
Uber’s tech stack is composed of a diverse set of mobile and backend systems working together to deliver reliable transportation worldwide. Riders and drivers alike use our website on web and mobile browsers to keep up-to-date on logistics, such as how to sign up for Uber or where to request a trip from a given airport, as well as regulatory compliance requirements and other region-specific information.
To effectively serve Uber’s global user base local teams create and publish content specifically for the riders and drivers in their city or region. To make the content creation process streamlined and scalable, the Uber.com Content Platform team developed Chameleon, a global content management system (CMS) for Uber.com. Named after the colorful reptile known for its ability to adapt to its environment, Chameleon allows teams to deliver content tailored to their regional markets, and provides an intuitive user interface (UI) so that anyone at Uber can easily build a customized web page.
In this article, we offer a deep-dive look at Chameleon, its multi-layered architecture, and how it enables regional operations and marketing teams worldwide to ship sleek, on-brand, and easy-to-navigate web pages.
Motivation behind Chameleon
To keep up with operational growth, many regional operations teams were creating their own microsites on multiple platforms. At their peak, there were as many as 1,500 Uber microsites on the web.
This approach proved problematic and confusing for users looking for official information from Uber. These microsites also decreased business efficiencies and search engine optimization (SEO) value while weakening visibility and controls.
To address these issues, the Content Platform team built a solution that integrated all microsite content into the Uber.com domain. To encourage migration to our CMS, however, we needed to make adoption seamless by ensuring that the solution could holistically integrate with the tools our city teams already used.
Why build a CMS from scratch?
Before Chameleon, Uber.com hosted several features that needed to remain performant as we transitioned into a larger system that could support its use by our hundreds of city teams. These included integration with existing Uber back-end services, Morpheus experimentation, and statistics and monitoring to help Content Platform engineers find and fix production issues.
While we considered existing open source tools, we decided it would be best to create a CMS on top of Uber web platform technologies. Building a CMS using an open source technology l would have required us to build many features—metrics, monitoring, security, and back-end service integrations—that already come standard in Uber’s existing web framework. On the flipside, creating an in-house CMS from the ground up gave us the opportunity to build a UI easy enough for city teams to pick up and start using with limited hiccups.
Architecting our own CMS also gave us a custom solution capable of flexibly integrating with our existing website. We chose to use React so that we could build interactive content blocks and integrate them tightly with existing components on the Uber.com domain. We also defined a JSON document format and built Chameleon-Render to render JSON documents to React components. This allowed us to better support content compared to some of Uber Engineering’s legacy CMSs, which allowed authors to write HTML and templating code directly into the system. With React components and JSON documents, Content Platform engineers can make patches to enhance accessibility, apply brand updates, and improve page load performance without touching the content, making it easier for us to continue improving the CMS well after launch.
Chameleon also supports geolocalized content. This means Chameleon enables operators to not only create regionally-tailored content, but also promote local content on general Uber.com pages. Take our Mexico City microsite as an example: a user who lands on our top-level driver page for Mexico will be prompted to click on the promotional block that directs users to the Mexico City driver guide. (We go into greater detail about how this system works later on.)
Next, we take a look at the Chameleon architecture, identifying key components and how they work together to address both the universal and regional needs of our users.
The Chameleon architecture is composed of four key product areas and a number of underlying services. At a high level, storage, a generic renderer, a web editor application, and the website itself serve as the basis of our CMS solution:
- Storage. The storage layer stores content as a series of blocks, which appear to the user as horizontal slices of each page. Storage is handled by Louvre, an Uber-architected service written in Go that sits on top of our Schemaless datastore.
- Chameleon-UI. We developed Chameleon-UI as our web editor application. This service reads block schemas defined in Chameleon-Render and generates an editor complete with live previews of the content being edited. Chameleon-UI also provides an interface to edit page-level configuration data, such as page title and description. This configuration is stored in Flipr, an Uber configuration service.
- Uber.com website. Finally, we integrated Louvre and Chameleon-Render with our website, delivering content to driver partners and riders around the world. Our website service is called uber-com. The location is detected using GeoIP and localized content is provided by Louvre.
Below, we provide an overview of Chameleon’s architecture:
Key system components
To understand how these different pieces fit together, we have to explain the fundamentals of storing and rendering content in Chameleon. First, we make a visit to Uber’s Louvre to explain what blocks and block contents are in the context of our system. We will also assess how Schemaless gives us the capability to read and edit content anywhere in the world easily and reliably.
We named our content datastore Louvre because, like the famed French art museum, it stores our masterpieces. As discussed earlier, Louvre stores content in Schemaless and uses TChannel to communicate with its clients, which are mostly CMS editors and web servers.
Content in Louvre is defined in terms of blocks and block contents. In our system, a block encodes a hierarchy associating concrete pages and geolocations to block content, which represents a JSON document which is typically rendered with Chameleon-Render. Each block content has a draft version and a published version.
When a request to resolve content is made to Louvre, it reads the parameters of the request and follows the hierarchy to find the correct block content. For instance, the first block on the driver signup page for San Francisco might be named city_driver_guide.1. The website would make a request to resolve city_driver_guide.1 with geolocalization parameters [“US”, “san-francisco”]. If the request was for the Mexico City driver signup page, the website would request to resolve the same block city_driver_guide.1 with geolocalization parameters [“MX”, “mexico-city”] and would receive different block content in the response. Louvre is also responsible for checking if translations are available for each string in the requested content and interpolating them in the result.
Figure 2 illustrates the content hierarchy for one example block, in this case city_driver_guide.1, below:
This system of blocks and block content allows us to create templates for new city pages without having to write the content first. When a new city launches, it is automatically assigned a website on Uber.com that city teams can then customize to provide highly relevant content for local riders and drivers. This functionality ensures that Content Platform and city teams can create and ship web content quickly, facilitating Uber’s continued growth and scalability.
Chameleon pages are designed by choosing blocks and laying them out one by one from top to bottom, like a club sandwich. Each Chameleon-Render block or block type is responsible for laying out the content inside of one layer of the sandwich. Block types such as billboards, promotions, calls-to-action, and disclaimers must support all the features of Uber.com, including a fully responsive design, accessibility, high-performance animations, and right-to-left localization. Since block types are pre-written and templated, Uber employees can easily create high-quality web pages in Chameleon.
If rendering content was all it could do, Chameleon-Render would not be very useful as a generic block library. To make the content creation experience as seamless as possible, we also needed to make sure that each block type can be edited with a form in the editor and that the form supplies intuitive controls for each field.
One obvious option would have been to implement a new form for each individual block, but this method is inherently error-prone and difficult to scale. Instead, we chose to delineate each block type with a schema JSON file that describes all of its editable properties. The UI then generates an appropriate form based on the schema. This way, we can build relevant, user-friendly forms for editing new block types written in schema as opposed to code. Moreover, these schemas are automatically validated in the Chameleon-Render build, which ensures that the editor will be able to generate the correct form for any given use case.
Chameleon-UI is Chameleon’s web editor application. Uber employees use this tool to create and edit content for their teams’ specific web pages. Below, we depict how users can leverage Chameleon-UI to create microsites and web pages:
Several features of Chameleon-UI make it easy to launch and customize specific web pages. These include integration with Chameleon-Render and a block editor to produce forms for editing block types, the ability to preview pages before they go live, and controls for both SEO-relevant properties of the page (such as title and description and operational settings like whether or not the page is live or should be indexed by search engines.) Chameleon-UI integrates with back-end translation services so that content can be translated to address Uber’s global community of riders and drivers.
Chameleon at Uber: now and in the future
Chameleon was designed, developed, and launched on a tight timeline to keep up with Uber’s pace of growth. In only six months, we went from initial design and prototyping, to launching with general availability in cities worldwide. But launching the platform was only the first part of the journey. The next and perhaps most important step was familiarizing Uber employees with using Chameleon so that they would feel comfortable transitioning their microsites to Uber.com.
To accomplish this, we first hosted a Chameleon training summit for employees from North America at our SF office to help them get up to speed with the system. Next, we took our show on the road, visiting offices in Mexico City, Amsterdam, and Singapore to train city teams in each of those regions. In addition to introducing the platform in an approachable and hands-on manner, these sessions were an incredible opportunity to seek out diverse perspectives and accrue user feedback for future improvements.
Based on this feedback, we implemented additional features to improve Chameleon’s UI and make it easier to create pages that were more dynamic and data-driven. Examples of new features include:
- Chameleon Fetching Entity (CFE). This new tool enables teams to add modules to fetch fare estimates, providing riders a sense of how much a given trip will cost, i.e., from San Francisco’s Financial District to Union Square.
- Accelerated Mobile Page (AMP) pages. The AMP project enables the creation of websites that are consistently fast-loading, performant, and visually striking; to implement AMP pages for Uber.com across all devices, we added a new front-end service for rendering AMP pages. This service pulls content from Louvre and configures the Chameleon-Render blocks so that they product AMP-compliant markup. (Check out AMP page for using Uber at San Francisco International Airport as an example.)
Other features and capabilities we intend to launch on Chameleon include:
- Migrating Chameleon-Render blocks to Web Content Blocks, our next-generation rendering system also used by our AMP site.
- Building high-level configuration tools to make Uber.com sites easier to operate.
- Developing a platform so that other Uber websites can use Chameleon’s CMS technology.
If helping teams create websites as customized (and colorful!) as a chameleon appeals to you, consider applying for a role on the Uber.com Content Platform team. We are currently hiring engineers for front-end and back-end positions in our San Francisco office.
Ken Sheedlo is an engineer on the Uber.com Content Platform team in San Francisco. When he is not creating delightful frontend experiences, you can find him exploring the city’s restaurants or doing yoga in the park.