May 20th, 2026
heart1 reaction

Your dev loop is full of tribal knowledge

Distinguished Engineer

Teams often treat developer experience as work that competes with shipping the product. That is understandable, but it is also backwards. The local loop, setup, debugging, repeatable operations, and agent workflows are part of how the product gets shipped. When those things are slow or unclear, every feature pays the tax.

The tricky part is that this tax is easiest to ignore when you are farthest from the dev loop. The people making prioritization decisions often are not the ones resetting the database, chasing logs across terminals, fixing a broken local environment, or trying to get an agent enough context to reproduce a bug. The pain is real, but it is distributed across the team in small, forgettable cuts.

The hard part is not wanting better devex. The hard part is making it real. The real architecture of many systems lives in Slack threads, shell history, half-remembered README steps, and the senior engineer who knows the order to run things. Turning that into something repeatable, discoverable, safe, and obvious for humans, scripts, and agents is work.

Aspire is built for that work.

Most modern apps are already distributed, even if teams do not describe them that way. A frontend, an API, a database, a cache, a queue, an auth provider, or another service is enough. Nobody sits down and says, “we are building a distributed system now”; the dependencies accumulate, and the local development experience gets harder every time.

Aspire’s bet is that a model for those dependencies makes devex easier to improve as the app grows. Humans use the dashboard. Agents and scripts use the CLI. Both are working from the same app model: the same resources, health, logs, relationships, and operations.

The adoption path is incremental. The dashboard can start standalone, the AppHost can begin as one file that describes what already exists, and commands can wrap scripts you already have. You do not have to throw away your README or current tooling to get value; Aspire gives those pieces a consistent model to hang off of.

Start with a place to look

The first pain is usually visibility. Something is broken, the API logged an error, the browser shows a failed request, the trace is somewhere else, and a developer is flipping between terminals, DevTools, and maybe a log aggregator. An agent can edit the code, but it has no structured way to understand what the running app is doing.

The smallest version of Aspire is just the dashboard.

aspire dashboard run

Run the standalone dashboard, wire your app to send OpenTelemetry data to it, and now you have a place to look before adopting the whole Aspire model. The signals your app already produces become something humans can inspect and agents can query through the CLI.

aspire otel logs api
aspire otel traces api

The first win is not “I modeled my whole system”; it is “I can see what is happening.” Logs and traces stop being scattered context and start becoming a queryable surface both humans and agents can use.

You can stop there for a while. For many teams, shared visibility is already a better local loop.

Give the app a shape

Once the team has a place to look, the next pain shows up: the dashboard can show what happened, but the app itself is still mostly implicit. That is when describing the application starts to matter.

aspire init

aspire init gives you the skeleton for an AppHost. This does not change your application; it describes it.

The AppHost is where Aspire starts to understand the shape of the system: this project talks to that database, this frontend depends on that API, this service should wait for that database, these resources belong together.

const builder = await createBuilder();

const db = await builder.addPostgres('db');
const api = await builder.addProject('api', '../api')
  .withReference(db).waitFor(db);

const web = await builder.addViteApp('web', './web')
  .withReference(api).waitFor(api);

await web.withBrowserLogs();
await builder.build().run();

Now the app stops being a collection of processes, ports, scripts, and tribal knowledge. It becomes a set of named resources with relationships, exposed consistently through the dashboard and CLI.

This is also why the model is code. Application modeling eventually needs real software engineering: reuse, composition, conditions, APIs, and extension points. Static manifests are useful, but once the model starts describing behavior and workflow, code is the more natural place for it to live.

A distributed app can be as simple as a browser calling an API; once that relationship is modeled, browser console logs and network requests can show up alongside backend logs, traces, resources, and health. The model gives the dashboard more context, and the local loop starts to feel different: you are operating the app as an app.

aspire start
aspire ps
aspire stop
aspire start --isolated
aspire wait api --status healthy --timeout 120

A script that waits for real health instead of sleeping for 30 seconds is not glamorous, but it is the kind of thing that makes a team faster every day.

Turn repeated work into operations

Then the next familiar pain shows up: the things everyone does, but nobody has really modeled. Seed the database, clear it, run migrations, issue a test auth token, create a test user, purge a queue, replay a webhook, run a smoke test, or open an admin UI.

Most teams eventually express these as scripts plus a README:

### Reset local database

Run `./scripts/db-reset.sh`, then `./scripts/db-migrate.sh`.
If you need a test token, run `./scripts/token.sh --user test@example.com`.
Make sure the API is already running first.

That works until it does not: the README drifts, the script names are inconsistent, the human knows the intent, and the agent sees a folder of shell scripts and guesses. Humans can compensate for undocumented systems with memory and experience; agents need structure.

Once Aspire knows about the resources, you can attach the common operations to the resources they affect.

await api
  .withCommand('migrate-db', 'Run migrations', runMigrations)
  .withCommand('reset-db', 'Reset local database', resetDatabase)
  .withCommand('issue-token', 'Issue test auth token', issueToken);

Those commands show up on the resource in the dashboard for humans. The same commands are available through the CLI for scripts and agents:

aspire resource api migrate-db
aspire resource api reset-db
aspire resource api issue-token

The implementation can still call existing scripts under the hood, but Aspire gives the operation a place in the application model: name, display text, resource context, dashboard affordance, CLI entry point, and a discoverable surface agents can invoke.

Commands are where developer experience stops being a paragraph in a README and becomes part of the application.

That is also where the agent story becomes concrete. An agent does not need to infer the reset flow from prose; it can see that api has a reset-db command, run it, wait for the API to become healthy, and inspect the logs if something fails.

This is when it starts to feel like a paved path

This is where the small pieces add up. Teams need shared visibility, repeatable operations, health checks, onboarding, debugging, and enough context for humans, scripts, CI, and agents to do useful work. Aspire connects those pieces through the app model: humans get the dashboard, automation gets the CLI, and both stay grounded in the same source of truth.

None of this replaces product work or the tools the team already likes. Scripts, traces, dashboards, editors, and CI can all still be useful. Aspire’s role is to connect those pieces through one model, so the team can start with visibility, add shape when the app needs it, codify repeated operations, and let the model grow only as far as the application needs it to grow.

Applications already exist as systems. Aspire makes those systems explicit, because explicit systems scale better than tribal knowledge.

Author

David Fowler
Distinguished Engineer

David Fowler is a Distinguished Engineer Software at Microsoft.

0 comments