# Microframeworks in the Admin Console

> Asana's admin console was a tangle of bespoke logic — until the team built declarative frameworks that made AI-assisted migration possible. Read how they shipped a month early.

Source: https://asana.com/inside-asana/microframeworks-admin-console

## Microframeworks in the Admin Console

Every Asana deployment has an admin console. It's where IT admins configure how their company uses Asana, such as adjusting password requirements, roles and permissions, whether files can be attached from Dropbox, and who can see a new project by default.

As Asana grew, the admin console accumulated years of custom logic and one-off patchwork, making administrative controls increasingly expensive to build and maintain. Take one administrative setting: the default privacy for new projects. An admin picks whether a new project starts out visible to the whole organization, visible to its team, or private to invited members. Simple to describe, but there's a lot of complexity hiding within:
- Is this feature in the customer's plan?
- Did they used to pay for it, stop, and get stuck on a setting they can no longer change?
- Are they a HIPAA or FedRAMP customer, where only elevated roles can edit it?
- Is one choice made unavailable by some other setting?
- Should an info banner be shown to describe the current limitations?

Any team wanting to add an admin control had to get all of that right. Most decided it wasn't worth it, and over time, the gap between what Asana could do and what an admin could govern widened. This is illustrated in that some controls can only be configured at the company-wide level, making it difficult for IT admins to apply the control to just a subset of their users.

Here's a snippet for the project privacy setting dialog, used to determine whether it should be disabled and if a banner should be rendered:

There's a lot of logic to parse through there - feature licensing, governance, overrides due to deployment structure, and user roles, especially for reviewers. To be diligent, you'd have to rebuild the test matrix in your head to determine whether it's correct.

And that's just the dialog. Whether the row even shows up on the settings page was decided somewhere else, and also inconsistently:

Three rows, three mechanisms, and the gating isn't always in the same file. So to answer "which settings does this customer actually see?" you not only had to go read every row, you had to scan every component. That question comes up pretty often: customer support trying to explain why a setting disappeared for a customer, a PM who wants a straight answer on whether a new control is a quick change or a two-week one, a new hire trying to find the one place that decides what a specific user can see.

Zooming out, four things made working in the admin console expensive:
- **Costly to review.** Logic lived wherever the author put them, so a PR could introduce special behavior without it being obvious to a reviewer, and correctness wasn't something you could easily verify just by reading.
- **Lack of standardization masked bugs.** We had long-standing bugs that were hard to catch. Many were due to drift between product specification and implementation, caused by a dense sprinkling of bespoke implementations. Teams made arbitrary decisions, leading to every control having their own quirks.
- **Costly to change.** To make one change for the end user, you had to find every place a rule was encoded, and there was rarely a single point of definition.
- **Costly to test.** Setting up testing required deep knowledge of the backend states, and comprehensive manual testing of end deployments was infeasible due to the number of interacting dimensions.

## Introducing the frameworks

We created a declarative framework for administrative controls to serve as the source of truth in the codebase. A control now states what it is:

Every field here maps onto a branch from the dialog above: requiredAdminRole is the HIPAA/super-admin check, upsellBehavior is the two upsell branches, and churnBehavior is the churned-customer case, letting them restore the default and nothing else.

As part of this, the framework exposes hooks which engineers use to derive the computed state of the control. Take a look at what the same project privacy dialog looks like now:

The banner if-chain collapsed into one shared component driven by a centralized hook. The framework handles the combinatorial logic of all the different scenarios, and the SMEs responsible for maintaining it, who understand the administration product deeply, can confidently make sweeping changes. Now we use strict typing to guide implementers to fill in the mandatory information required to correctly render their setting in all possible scenarios. Crucially, they don't need to understand the intricacies of those scenarios, or how they interact.

These settings are accessible via rows in the admin console UI. The visibility of those rows got the same treatment, and this is where the second framework comes in. A row in the settings registry doesn't describe its own visibility rules, instead tying it to the controls that represent it:

The controls array is the value add. It holds the same ProjectDefaultPrivacy object the dialog hands to useAdminConsoleControl, and the registry runs it through the same source of truth, so the page and the dialog can't disagree. It used to be computed separately, so discrepancies could lead to two failure modes: a row that's visible but opens a dialog you can't use, and a setting a customer pays for with no row to reach it from. Centralizing it eliminated this category of bugs.

Tests which adopted the centralized frameworks improved PR reviewer experience greatly. For example, take row visibility testing, which answers the "which settings does this customer actually see?" question from earlier. Instead of test code, a scenario is just data: a persona, a domain state, and the pages it renders on.

And a row just lists which named scenarios it must appear in:

There's no render call nor assertion to write. A dynamic test suite reads the catalog and checks every row against every scenario it's named in. The catalog is now the one place that says what a customer sees, checked by machine . No more relying on a diligent code reviewer or the author to correctly identify and write their own test cases.

## In the world of AI

We started this work in late 2025 because we anticipated the need to enable non-SME engineers to build confidently in the admin console. At the time, the goal wasn't to optimize for LLM performance, but as it turns out, standardizing and streamlining the experience for engineers does the same for AI agents.

Before building these frameworks, we did throw AI at this migration problem, which technically worked. Issue was, neither the agent nor the reviewer could tell whether the tests were actually correct, which meant false confidence and silent gaps. AI doesn't fix the lack of structure, it just produces more code, faster, on top of whatever structure is already there. [Google made a similar case for Go's type system in AI-assisted development](https://developers.googleblog.com/why-go-is-an-ideal-language-for-ai-assisted-software-engineering/): static types act as an automated safety net, since LLMs are prone to hallucinating properties and mismatching types across files. TypeScript isn't statically strict the way Go is, but a framework can build the same guarantee on top of it: define the control's type once at the framework level, and every implementation has to fit it at the point of use.

Once the frameworks were ready, we started preparing to delegate and parallelize. I used our new spec driven development tool [link placeholder: eng blog post on spec driven development: [Asana Eng Blog - Spec-driven development: The Good Parts](/inside-asana/spec-driven-development) to build a skill that does the job from start to finish. It encodes the whole conversion: define the control, call the hook, replace the banners, update the fragments, add the new declarative tests, plus a self-updating checklist and log of edge cases from previous conversions. Across all ~150 migrations, 91% needed no revision after review.

Kicking off an agent to write a PR doesn't take much effort, and reviewing said PR doesn't either. Since everything is declared in a predictable manner, reviewers don't need to be SMEs in administration to check if the implementation matches the product spec. Crucially, this opens up the eligible reviewer pool to a much wider group of engineers, which speeds up velocity more than just a wider funnel at the top creating PRs. We're not alone in rethinking review for this era: [GitHub rebuilt Copilot's own review agent around structured PR evidence](https://github.blog/ai-and-ml/github-copilot/better-tools-made-copilot-code-review-worse-heres-how-we-actually-improved-it/) to help human reviewers get to the right questions faster, cutting their review cost by about 20%.

The original migration of ~150 items spanning multiple frameworks was scoped as manual, one-off engineering work from start to finish. Building the frameworks first, then delegating the migration to engineers monitoring agents afterward, let us ship the whole effort more than a month sooner than that original plan called for.

## What's next?

Building these frameworks was never a project in itself. It came out of necessity, from a roadmap that needed to parallelize and scale, with limited and changing staffing along the way, and without requiring every contributor to be a domain expert first. We've already seen it work past the team that built it: 18 of the 66 controls in the framework today were authored by engineers across 8 different teams.

Now, we're looking for the next place to make this kind of investment. If anything, the case is stronger now than it was before we started: a well-designed, declarative framework doesn't just make review easier, it decides whether an agent produces something reliable, or just something fast. It's also what might make autonomous review plausible: [Cloudflare has built a system where an AI reviewer approves clean code and blocks real problems on its own](https://blog.cloudflare.com/ai-code-review/), and that only works because their inputs are structured enough for the reviewer to trust. Whether ours are structured enough to try the same thing is a good next question.

#### About the author

Leo Zhang is a software engineer on the Admin Foundations team, empowering IT admins in governing their organizations. Currently, he’s improving the development experience of other product engineers in the Admin Console by investing in the technical frameworks that back our product.

#### Team Shout Outs

Designing, implementing, testing, and socializing these changes has been a huge team effort. This was made possible by the contributions of other engineers on the Admin Foundations team: Yunus Rahbar, Maryam Booshehrian, Saverio Castelli, Bronwyn Damm, Cindy Yu, Calvin Norton, and Jaxsun McCarthy Huggan. Walter Li from the agent success tiger team helped immensely in setting up the right AI tooling for these migrations.

### **References**
- Lan Cheng, Emerson Murphy-Hill, Mark Canning, Ciera Jaspan, Collin Green, Andrea Knight, Nan Zhang, and Elizabeth Kammer, "What Improves Developer Productivity at Google? Code Quality," ESEC/FSE '22, November 2022. [https://doi.org/10.1145/3540250.3558940](https://doi.org/10.1145/3540250.3558940)
- "Orchestrating AI Code Review at Scale," Cloudflare Blog, April 2026. [https://blog.cloudflare.com/ai-code-review/](https://blog.cloudflare.com/ai-code-review/)
- Napalys Klicius, "Better tools made Copilot code review worse. Here's how we actually improved it," The GitHub Blog, July 2026. [https://github.blog/ai-and-ml/github-copilot/better-tools-made-copilot-code-review-worse-heres-how-we-actually-improved-it/](https://github.blog/ai-and-ml/github-copilot/better-tools-made-copilot-code-review-worse-heres-how-we-actually-improved-it/)
- "Why Go is an Ideal Language for AI-Assisted Software Engineering," Google Developers Blog, August 2026. [https://developers.googleblog.com/why-go-is-an-ideal-language-for-ai-assisted-software-engineering/](https://developers.googleblog.com/why-go-is-an-ideal-language-for-ai-assisted-software-engineering/)

- [Spec-driven development: The Good Parts -- and what we learned after three months](/inside-asana/spec-driven-development)

Engineering

#### Staff Software Engineer

After three months, we had a clearer picture of when the added structure helped and when it got in the way.One of our engineers was preparing a data migration and decided to use s ...

- [We migrated off Enzyme in 2 weeks. It should have taken five years](/inside-asana/migrating-off-enzyme-2-weeks)

Engineering

We recently used AI to complete years of engineering work in about one sprint. Here's how, and why it's changed how we think about what's possible.The five-year problemBack in 202 ...

- [Breaking the Lethal Trifecta: How Asana Thinks About Agentic AI Security](/inside-asana/how-asana-thinks-about-agentic-ai-security)

Engineering

#### Staff Security Engineer

Agentic AI introduces a class of security risk the industry hasn't solved. Here's how we think about it at Asana, and the security invariants we hold across our AI features.The pr ...

- [Improving Asana’s Pageload Performance](/inside-asana/improving-asanas-pageload-performance)

Engineering

#### Senior Engineering Manager

Asana serves 2.5 million pageloads a day, peaking at 3.5 million. Each one is a user's first impression of the product — the moment between intent and action — and for a tool team ...

- [Microframeworks in the Admin Console](/inside-asana/microframeworks-admin-console)

Engineering

Every Asana deployment has an admin console. It's where IT admins configure how their company uses Asana, such as adjusting password requirements, roles and permissions, whether f ...

- [Engineering](/inside-asana/engineering-spotlight)
