Arc XP

The Washington Post’s cloud-native digital experience platform.

Arc XP


Software Engineering Intern


Jun. – Aug. 2022


Full-Stack Development

Microservices Architectures

Design Systems


TypeScript React Storybook AWS Jest


Arc XP is The Washington Post’s cloud-native digital experience platform that helps enterprise companies, retail brands and media organizations create and distribute content, drive digital commerce, and deliver powerful multichannel experiences. One of Arc XP’s core products is Arc XP Content, serving as an API-first agile content management system (CMS) that streamlines content creation and simplifies content management.

Clients looking to migrate from legacy CMS to Arc XP use a service called Migration Center. Migration Center is responsible for transforming their data into ANS (Arc Native Specification), a collection of schema documents that comprise the Washington Post’s definition of “content” . These documents include content such as stories, videos, and images.

As a software engineering intern on the Arc Migration Center team, I split my time working on Migration Center’s serverless microservices architecture and ANS Mapper, a new GUI that streamlines migration projects.

Migration Center

Migration Center performs migrations through an asynchronous and transactional process using AWS Step Functions and Queues.

  1. The user calls the Migration Center API
  2. API Gateway triggers a Lambda function that publishes the message to an SNS topic
  3. SQS queues for each ANS content type receive messages from SNS
  4. A poller service running in an ECS container retrieves messages from SQS and submits them to a step function for the corresponding ANS content type
  5. The step function handles migration and submission to Arc

Dead Letter Queue

SNS occasionally fails to send a message to SQS. This could occur when there is a bug in Migration Center or a client incorrectly uses the API, such as providing an invalid request parameter or body that results in a 422 Unprocessable Entity response status code. When a message fails to send, the unconsumed message gets lost, which makes debugging and retries quite difficult.

To resolve this issue, I added a dead letter queue (DLQ) that would capture unconsumed messages from SNS . I also configured a CloudWatch alarm to alert the team when the number of messages in the DLQ exceeded a certain threshold. These changes were made in CloudFormation templates so that every client was provisioned with their own isolated DLQ that could handle all ANS types.

The DLQ ensured that the team would always have access to unconsumed messages, making it much easier to debug errors. After bug fixes, the DLQ also enabled developers and clients to move unconsumed messages back to SNS in order to complete the migration process.

Optimizing Lambdas

While implementing the DLQ, I noticed that each Lambda function had a surprisingly large deployment package size. After some investigation, I discovered that each deployment package included the entire aws-sdk package, causing it to take much longer to download and unpack the deployment package ahead of invocation. Across over 30 Lambda functions, this significantly slowed down cold starts for the Migration Center API.

To reduce the deployment package size, I used the Serverless framework to create a Lambda layer, which allows the aws-sdk package to be shared across all Lambda functions. I also reorganized Lambda functions into separate directories and configured Serverless to package functions individually , allowing webpack to more effectively tree shake the project.

As a result, each Lambda function would have its own deployment package that only included the individual AWS services necessary for that function. In total, I reduced the average Lambda deployment package size from over 40 MB to just 1 MB .

API Testing

Before each new release, the team would hold a bug bash. We would go through a list of existing core features and new features for the release (such as the DLQ and Lambda changes), using Postman to ensure that all functionalities of the Migration Center API behaved as expected .

ANS Mapper

Client developers using Migration Center did not have a way to preview ANS before migration, which forced them to write a lot of transform code without being able to test its validity. ANS Mapper is a new tool that improves the process of transforming source content into ANS. It allows developers to use templates to create transformations with minimal code, then preview the mapped content in a GUI before migrating it to Arc .


After creating a new Next.js project, I started by setting up the project with CI/CD. We used husky to create a pre-commit hook that formats, lints, and tests code . We then used AWS CodePipeline and CodeBuild to install dependencies, build the app, and deploy the app to S3 and CloudFront.

UI Components

Ideally, we would have used components from Arc Design System . However, many core components were not available yet and did not have a roadmap for completion. To continue development on ANS Mapper while maintaining a consistent user experience across the Arc platform, I built UI components following standards set by Arc Design System , with the intent to later contribute to it. Therefore, components were built using the following technologies:

  • React Aria : Accessible UI primitives for design systems
  • React Stately : Cross-platform state management for design systems
  • Stitches : CSS-in-JS and design system tokens (color, spacing, typography)
  • Storybook : Component development, testing, and documentation

I began with development of the ANS Mapper home page. The core functionality on this page was a ListView component that supports lazy loading of migration projects . The component would initially load 10 projects, then load additional projects when the user scrolls to the bottom of the ListView. Once loaded, we kept data in a Redux store so that it could be detached from the component .

ANS Mapper: Project List ANS Mapper: Project List

I also worked on a NewProjectDialog component that appears when the “New Project” button on the home page is clicked. After all required fields are filled and the user clicks the “Start Project” button, the component calls the API to create a new project, then redirects the user to a Project Details view. For this dialog, I had to create these additional components: TextField, TextArea, Checkbox, CheckboxGroup, Icon.

ANS Mapper: New Project Dialog ANS Mapper: New Project Dialog

API Specification

ANS Mapper required a separate API to manage migrations projects, involving templates and source data. I defined a CRUD API specification for ANS Mapper domain objects using the OpenAPI Specification . This specification serves as documentation to developers and clients using the API.

The specification defines schemas for the following domain objects:

  • Project Model: A migration project in ANS Mapper
  • Map Model: A map (data transformation) used in a migration project
  • Map History: A map’s version history

For each request, the specification defines any request parameters, request bodies, and responses (status, description, body) . For example, the Project Model API includes the following requests:

  • Get list of projects: GET /project
  • Get a single project: GET /project/{projectId}
  • Create project: POST /project
  • Update project: PUT /project/{projectId}
  • Update fields in project: PATCH /project/{projectId}
  • Delete a project: DELETE /project/{projectId}