Spotify’s Backstage Portal cut my Claude Code token usage by 90% A developer reports cutting Claude Code token usage by 90% by routing routine I/O tasks to cheaper Gemini 2.5 Flash models via two custom modes in Spotify's Backstage Portal, with Gartner predicting AI coding costs will surpass the average developer's salary by 2028. The setup uses a 'bulk-reader' mode for file analysis and a 'code-writer' mode for boilerplate generation, both callable from the Portal CLI or API. Spotify’s Backstage Portal cut my Claude Code token usage by 90% Most of what an AI coding agent does for me isn't thinking. It's I/O. Reading five files to answer a question about one method. Generating a test file that follows the exact same pattern as the twenty test files next to it. Updating docs after a meeting. Thousands of tokens gone and almost zero reasoning. The seat license isn't what hurts, it's the tokens. And you're feeding all of it to a frontier model that's wildly overqualified. What if you could route the grunt work to something cheaper that handles it just as well, and save the expensive model for the problems that actually need it? It’s hardly just my problem. By 2028, AI coding costs are expected https://www.gartner.com/en/newsroom/press-releases/2026-06-24-gartner-predicts-ai-coding-costs-will-surpass-average-developer-salary-by-2028-as-token-consumption-surges to blow past the average developer's salary. A quarter of engineering leaders already burn $200–$500 per developer per month on tokens. Some are well past $2,000. The tooling pays for itself but only if you stop burning frontier tokens on work that doesn't need them. Turns out, the fix didn't require a platform team or a new subscription. Just two modes. Two modes, zero code This is exactly the kind of use case AiKA Modes https://backstage.spotify.com/docs/portal/core-features-and-plugins/aika/modes in Spotify’s Backstage Portal were built for. A mode is a declarative agent that runs on an ephemeral runtime - think AWS Lambda, but for agents. You define the instructions, pick a model, set parameters like temperature, and attach MCP tools. Portal handles the rest. No infra to manage, no API keys, no long-running servers. Modes are callable from the Portal CLI or API. They can be public shared with the whole company or private. For this router to work I created two modes. Both use Gemini 2.5 Flash as the worker model in the examples below, but the model field accepts any model you have configured in your Portal instance. Pick whichever works for you. Mode 1: bulk-reader For when Claude would otherwise read multiple large files just to answer one question. name: bulk-reader description: Bulk file reader for code analysis - delegates I/O from Claude Code instructions: You are a precise code analyst. Read the provided files and answer the question concisely. Output structured bullets only. No greetings, no prose, no preambles. Lead every bullet with the exact name, type, or line number. Use nested bullets for details. Skip anything the caller did not ask for. visibility: public model: gemini-2.5-flash resourceLimits: temperature: 0.2 tags: - coding - delegation Mode 2: code-writer For tests, config scaffolding, type stubs or anything where the output is predictable from existing patterns. name: code-writer description: Boilerplate code generator - delegates output-heavy work from Claude Code instructions: You generate code files based on a spec and reference files. Match the existing patterns, conventions, naming, and style exactly. Output only the code — no explanations, no markdown fences unless asked. If the spec is ambiguous, make reasonable choices that match the reference code's patterns. visibility: public model: gemini-2.5-flash resourceLimits: temperature: 0.2 tags: - coding - delegation That "output only the code" instruction matters. Without it, the model wraps everything in markdown fences and explanatory prose that Claude then has to parse through. Routing The first version of this was a block of routing rules in CLAUDE.md. It sort of worked: Claude would read the instructions and self-route to Portal. But it had problems. The rules were advisory, not enforced. Claude could ignore them. And every project needed its own copy of the instructions. The current version is a Claude Code plugin called shunt https://github.com/sorantis/portal-ai-plugins/tree/add-shunt-claude/plugins/shunt . Delegation goes through the Portal CLI actions registry so the plugin works against any Portal instance with AiKA plugin enabled. Layer 1: Hooks Claude Code hooks fire before every tool call. Shunt registers two PreToolUse hooks: check-file-size fires on every Read call. If the file exceeds a configurable line threshold default: 350 , the hook blocks the read and tells Claude to use the /bulk-reader skill instead. Targeted reads pass through - Claude already knows what section it needs. check-bash-read catches cat, head, tail, less, and more on large files. Piped commands cat file | grep pass through since those are targeted reads. The threshold is configurable via the SHUNT MIN LINES environment variable. Set it in your shell profile or in .claude/settings.json: { "env": { "SHUNT MIN LINES": "500" } } Layer 2: Scripts I have two bash scripts that wrap the Portal CLI calls. Claude calls a script with named arguments. The scripts handle everything internally: building the request, invoking the actions, unwrapping errors, and reporting token usage to stderr. Modes are addressed by name and resolved by Portal: case-insensitively, preferring your own mode, then your team's, then public ones. Fork the public bulk-reader into a customized version and yours automatically takes precedence - no configuration needed. bulk-read wraps each file in XML tags for clear boundaries and sends them to the bulk-reader mode along with the question. bulk-read --question "What does this service do?" --paths src/Service.java src/Handler.java Follow-up: ask again with the same paths bulk-read --question "Which methods call the database?" --paths src/Service.java src/Handler.java Every delegation is one shot. The invocation is ephemeral nothing is stored server-side and re-sending the files on a follow-up is free where it matters, because the corpus goes to the worker model and never enters Claude's context. code-write sends a spec and a reference file to the code-writer mode, strips markdown fences from the output, and can write directly to disk. Claude never sees the generated code. The reference is required: without a file to match patterns against, the worker would generate context-free code that fits nothing in your project. code-write --spec "Write tests for UserService" --reference tests/OrderTest.java --target tests/UserTest.java Output to stdout code-write --spec "Generate a config stub" --reference config/existing.yaml Layer 3: Skills Two skill files tell Claude when and how to call the scripts. Skills are markdown files with a description and usage examples. When the hook blocks a read, the block message points Claude to the /bulk-reader skill, which shows the exact invocation syntax. This layering means the system degrades gracefully. Even if Claude doesn't read the skill description, the hook still blocks the expensive read. The skill just makes the redirect smoother. The benchmarks Tested against a Java monorepo across four scenarios, measuring tokens Claude would consume reading files directly vs. consuming the bulk-reader's summary or writing code via the code-writer. Mean bulk-read savings were around a whopping 90%. The code-write scenario is harder to measure in tokens because without shunt, Claude both reads the reference files and generates the output as expensive output tokens. With shunt, the code goes straight to disk, Claude never sees it. What doesn't work You can't delegate editing. The worker model's summaries don't include reliable line numbers. If Claude needs to make edits based on the analysis, it still has to read the specific section directly. The hooks allow targeted reads with offset/limit for exactly this reason, so delegation saves tokens on understanding. You can't delegate reasoning. The worker model found surface-level patterns but missed a subtle thread-safety bug in my testing. Claude spotted it in seconds once given the right context. The routing explicitly excludes debugging, architectural decisions, and safety-critical code. Latency adds up . Each delegation is a network round-trip: Claude Code to the Portal backend to the worker model and back. Responses typically take 10–30 seconds, and Portal caps a single invocation at 30 seconds, so very large generations need to be split into smaller calls. This is acceptable for large reads, but counterproductive for small ones. The line threshold exists for this reason, below it the overhead of delegation exceeds the savings. Token savings are just the starting point The plugin is a Claude Code artifact, but the idea underneath is model routing powered by AiKA modes. The modes are the load-bearing piece: They're reusable. The same bulk-reader and code-writer modes work across every project and every tool that can shell out to the Portal CLI. They're shareable. Both modes are public in AiKA. Anyone can use them today without creating their own. They're composable. You could create a doc-writer mode for documentation, a reviewer mode for code review summaries, a translator mode for i18n. Each one is a few clicks away. They decouple the routing decision from the worker. The plugin decides when to delegate. The mode decides how to respond. Swap Gemini Flash for a cheaper model, change the system prompt, add MCP tools - the plugin doesn't change. This is the real power of AiKA modes: they turn model routing from a systems engineering problem into a configuration problem. You don't build infrastructure. You describe what you want and name it. Try it yourself Install both plugins from the marketplace: spotify/portal-ai-plugins claude plugin marketplace add spotify/portal-ai-plugins claude plugin install portal@portal claude plugin install shunt@portal. The portal plugin provides the Portal CLI that shunt delegates through. In a new Claude Code session, run /portal:setup to set up and authenticate the Portal CLI against your Portal instance. You’re good to go, just ask a question that spans multiple files. The bulk-reader and code-writer modes are already public, so there is nothing to create. If you want to customize them - different worker model, different instructions - fork them in Portal and your version takes precedence automatically. The modes are reusable across projects and shareable with your team. The plugin enforces the routing so you don't have to think about it. Learn more about modes https://backstage.spotify.com/docs/portal/core-features-and-plugins/aika/modes modes .