Staff Software Engineer · 2017–2026 · San Francisco · Remote
Applied AI, agents, and the platforms other teams built on.
AdRoll is an advertising platform: one place for a marketer to run display, social and email campaigns. NextRoll is the company behind it, which also runs AdRoll ABM, the B2B sibling brand.
A colleague from both Rdio and Pandora referred me here. AdRoll has been used by over 140,000 brands and handled 120 billion ad auction requests a day. I joined the Ads team as a senior engineer while AdRoll was breaking its monolith into services, and I brought what that work needed: I had built ad products at Bebo and front ends at Rdio and Pandora. The migration was recomposition, not refactoring for its own sake: keep the best existing piece of each system, make it a durable service that does one thing well, and use the right tool instead of reinventing it. That became how I worked for nine years, from the shared frontend systems our product teams adopted to the AI-assisted engineering workflows and the products built on them.
- React
- TypeScript
- Design systems
- Python
- Developer tooling
- AI agents
Key projects
Selected work from the AdRoll years.
Engineering agents: EngHelper and Monet
Engineering agentsEngHelper and Monet were the agents teams reached for in Slack: one for engineering questions and pull-request work, the other for design and branded output. They were built the same way, ran on the same server and shared one context folder: repository maps, design-system tokens, Confluence keys, channel purpose maps. A capability added for one was available to the other, which is why the second went from proposal to running in under 48 hours. I defined Monet's SOUL.md and wrote most of its guardrails: how it reaches for the design system, how it sounds, and what it refuses.
I owned the safety and governance layer of the engineering agent and became its second-largest contributor. I turned our AI agent security policy into a shared skill that classifies any agent by risk tier, and wrote the classifications for four of our systems. With the EngHelper team I added capabilities to the service that brokers what the agent may touch. When the agent gained the ability to open pull requests, I classified that access as critical-tier under the lethal-trifecta model. Ten days later I shipped a kill switch that disables writes while leaving read-only investigation working, and a CI test that fails on any unclassified write endpoint. The test caught four new endpoints as the broker grew. Trust, but verify: every pull request the agent wrote carried provenance, so each could be audited against our SOC 2 controls.
In its first month, 30+ colleagues used the agent, the CTO, SVP of Engineering and VP of Product among them, to resolve support and triage issues faster. Over the same month, 47 of its pull requests from Slack merged across five repositories. I also built and shipped 411Bot, another Slack agent on the same runtime that answers who owns what across the org.
EngHelper and Monet shared a skill pipeline that turned source code into architecture documentation, decision records and technical reviews, with arc42 as its standard. I wrote the architecture skill, the one that reads a repository and produces the document. It kept strictly to arc42's twelve numbered sections, C4 diagrams and Architecture Decision Records, so documents could be compared across repositories. It added two rules of its own: a generated claim needed a source reference, and code behind a feature flag was not proof of live behavior. During Hack Week, teammates ran the skills from Slack on their own projects, and the reviewed docs landed in six repositories.
I was the largest contributor to our shared agent-skills repo. I wrote its rule that no skill merges without a documented eval: each new skill's pull request documents an invocation, input, output and pass/fail judgment. I also wrote its S/M/L sizing convention, enforced by a linter and carried by 125 of 128 skills. I audited the repo against the SVP of Engineering's nine AI principles, set the auto-invoke policy, and turned auto-invoke off on 54 skills.
Working with agents, I merged more than twice as many pull requests in Q2 2026 as in any earlier quarter since 2017, across 21 repositories, from our design system to our internal agent.
Creative tools advertisers used
Creative interfacesI built the front-end integration for AI-generated ad copy, and the front end for AI image outpainting with the Creatives team at Hack Week 2024, both in Create Web Ads. By fall 2025, about 250 advertisers were spending on ads built with it.
The problem underneath stayed the same for eight years: an advertiser has creative in a few sizes and the exchange has inventory in many more. In 2018 I built the seam-carving core of a Hack Week project that stretched existing flat ads into adjacent sizes to close that gap. It placed at Hack Week, shipped as a beta tool, and found the next ceiling: the source image itself. Out-painting lifted the ceiling by extending the imagery instead of distorting it, and templates moved the question again: from adapting the creative a company already had to producing it for the ones that had none.
The last ceiling was the assets themselves. My AI ad-builder prototype worked from our crawl of a customer's website and built a full brand profile from it: styles, fonts, colors, images and voice. At that point we had all the pieces, and what was left was implementation and where the context came from. So I exported our templated ad preview as a standalone component the AI Assistant team could embed in their chat, which already held the brand, the campaign and the assets to pipe into templates.
Underneath the builders, I enabled WebP creatives across our ad platform: WebP asset handling in the ad sync pipeline, no more WebP-to-PNG fallback, and a fix in our Go image service for the transparency loss WebP exposed.
Media Planner for sales conversations
LLM deploymentA colleague's static prototype showed what an AI media planner for sales reps could be. I took it to production inside the AdRoll AI Assistant, for reps preparing prospect pitches: I iterated on the prompt, wired in the product manager's planning heuristics, built the tool integration and the interface on the design system and shared components, and drove the release with the AI Assistant team. I kept it apart from the host application's navigation so the product team could open it to advertisers whenever they chose. Every plan had a fixed shape, with channel choices, budgets and assumptions spelled out, because a rep has to defend those numbers in a room.
I wrote its prompt-test protocol and ran it, testing every prompt revision against sample briefs and a pass/fail checklist.
A shared platform 28 repositories depended on
Platform modernizationI maintained and extended Rollup, AdRoll's shared React component library (an internal name, no relation to the bundler), along with the tooling that distributed components to their consuming applications and owners. It's the same library I moved to React 15 with a Hack Week team in my first year. I worked on it in every one of my nine years there and left it better than I found it. 28 repositories across two product lines depended on it, running versions that had drifted far apart. The drift was the problem, not the library. Dependency inspection, generated upgrade pull requests, and release tracking gave teams a common way to plan and adopt changes; codemods did the mechanical part. I kept coming back to the dependency crawler behind that inspection, and each pass grew it toward a graph of the company's repositories, their dependencies, owners, Jira boards, Slack channels and org chart. I also worked to keep the graph from drifting. Ownership was derived from CODEOWNERS once hand-kept mappings drifted, and a schema-backed org chart in a source-of-truth repo shared with security let every mapping be reconciled against other systems.
I used that infrastructure to coordinate frontend work for the 2025 AdRoll rebrand, then extended it with migration guidance and effort tracking for staged React upgrades. For React 19 I mapped the consuming repositories' dependency-update chains, each on its own React version and mix of adjacent libraries. I built and ran the program while other engineers landed the individual upgrade PRs. Agents sequenced each task along its dependency chain from the dependency and ownership data, and sized it against the owning team's past execution and known blockers. CI pipelines delivered it to that team as a pull request ready for review and deploy. No app ever got more than one major ahead. I re-planned the estimate from live data four times in ten days. The work, entered at Hack Week 2026 as React 19 Reckoning with three teammates, received Overall runner-up.
From the Front End Core seat I also ran our Core Web Vitals work. I tested and documented Datadog RUM and got the teams to install it and report back. I set up the dashboards and followed up with each team, and LCP and CLS were on the SLA. Some of the fixes I made myself: cutting SCSS bloat, and a year-long font-loading refactor that finally took the fonts out of the shared style package.
Bringing AI prototypes into the design system
Design systemStyle rules were spread across thousands of files and, until the rebrand, two design systems, so even a small change had an unknown cost, and design-system requests got pushed back. Measure twice, cut once: I built a Claude Code skill that measured everywhere a change would land, so the cost was known before anyone said yes. A button corner radius touching hundreds of buttons, the kind of change we would have pushed back on a year earlier, was analyzed and shipped the same day, and design and engineering could move on system-level changes with confidence again.
AI-assisted prototypes could demonstrate an idea before they fit the existing product. I worked with designers to bring those interfaces into shared tokens and components, and moved their routes into the design-system workspace. To make each one reviewable, I wrote the Terraform for keyless GitHub Actions deploys to AWS and onboarded the repo to our EKS preview platform, so a branch became a live preview URL inside its pull request.
What made the difference checkable was a snapshot gallery: a full mock-up of the product (onboarding, campaigns, analytics, ad creation, an assistant), with every view under test, so a gap between a prototype and the shipped design system showed up as a picture rather than an argument. One of those prototypes was a product manager's insight dashboard over Gong, Jira, Zendesk and Cube. I took it from prototype to ready for handoff in a week: I moved its credentials to 1Password, added a server test layer, prepared its CI, purged customer data from its docs, and brought it into the company's product-AI repo. Then I turned its sources into shared agent skills, and support ran its Zendesk analysis from Slack.
Reading 100 production errors for the few that mattered
Production reliabilityIn March 2026 I pulled all 100 unresolved Sentry issues in the ads service and weighed each one by who it hurt and whether it could be fixed, as well as by how loud it was. The two loudest, 3.5 million and 1.1 million events, were most likely outside traffic and invalid client data, so I left them and wrote down why. Three quieter problems were doing real damage. An N+1 query slowed ad filtering to about three and a half seconds for 309 users. A race in label creation threw 500s every day, more than 11,000 events. A background sync failed silently for an entire advertiser whenever it met an ad size it didn't recognize.
I wrote six tickets that day, each with its evidence and a proposed fix, and fixed three of them myself within three weeks: the label race, the silent sync failure, and a crash on video ads with no video URL, which got a regression test.
Figma to HTML5 ads
CompanionThe Figma HTML5 Ads Builder plugin was a separate, internal project from our September 2023 Hack Week, built with a cross-functional team. It brought reusable ad layouts into a design tool and won NextRoll's 2023 H2 Hack Week Product Award.
Deciding what to build
ShapingAdRoll ran an adapted Shape Up: every proposal went to a betting table, and in one cycle 19 of 47 got funded. Work that reached that table needed a shape first: an appetite, a real scope, and the rabbit holes found before anyone bet on it. In my last two years I shaped the work as well as built it: I led a four-phase AI-native roadmap for the Creatives team, co-wrote three pitches the betting table funded in one cycle, and made my case with working prototypes.
That roadmap ran from a brand manager and reusable templates to a multi-brand scraper, then conversational ad creation. For an AI ad-creation tool the material existed but was scattered across design files, documents and meeting transcripts. I built a working front end in hours instead of synthesizing for weeks. It made the idea concrete enough to argue with, and it exposed what was left. For advertising inside ChatGPT I wrote the interface and data-model specs, then checked them against the code we actually had. That caught what a document review would miss: it needed a new ad type rather than a reuse, it had to follow the identifier conventions already in place, and one grouped ad could carry hundreds of variants.
The ChatGPT channel was announced a few weeks after I left.
Finding out which ad worked
ExperimentationAdvertisers could make ads with us and then had no way to tell which one earned its impressions. I built the front end of A/B Creative Tests: the builder that pairs two variants across ad sizes, the routes and navigation that gave it a home in the product's Creatives section, a demo page teams could try it in, and the instrumentation that showed how the beta was used, working with the backend and data engineers who built the experiment engine.
An early build announced a winner, one variant up 11.56%, right above its own table calling the result not significant. The percentage is what a reader acts on, so that had to change. A test now reads as Collecting Data, Too Close to Call, or Winner Determined. Too Close to Call is the important one: it tells the advertiser the difference could still be chance. A test has to clear a 95% probability that one variant is best before the surface names a winner. We shipped it as its own page rather than inside the campaign-creation flow, which is what got it out the door; folding it into that flow is what the roadmap wanted next. It went out as a closed beta for a small group of high-profile advertisers who had asked for it. I ran access through a Split segment, and when interest kept widening, we froze new access and I asked product to own who got in.
Hack Week
Organizational leadershipI also produced NextRoll's twice-yearly Hack Week fourteen times, bringing people from different teams together to try ideas and share what they learned. Two of the things above started in one: the architecture pipeline and Monet. I entered it before I ran it and kept entering after: seven placements, four of them wins.
The limits were part of what I built. I made an uncertain A/B result legible instead of naming a winner, and gave engineers a way to stop an agent that could write code. In each case, the person using the tool could see what to trust and make the call for themselves.