DEV Community

Cover image for I Don't Need Smart AI to Build Apps. Here's Why.
localeostackio
localeostackio

Posted on

I Don't Need Smart AI to Build Apps. Here's Why.

I stopped reaching for the most capable model for every app. With TDK CLI, the infrastructure is already decided, so a simpler agent only has to get the application logic right.

For a long time I assumed that building an app with AI needed the best model available. Every project got the most capable assistant, the longest prompts, and the most careful review. Most of the effort went into getting the infrastructure right, not the features.

I stopped doing that. I now use simpler agents for most of my app work, and the results are good enough that I rarely go back. The reason is not that the simpler models got better. It is that TDK CLI removed most of the decisions a model used to get wrong.

This article explains what that means, where the limits are, and three prompts I use with a less capable agent.

The real problem was never the features

Ask a model to build a small app and it can write a handler, a component, or a query well. The trouble starts around the code:

  • a Dockerfile that builds but runs the wrong command,
  • a docker-compose.yml whose ports disagree with the proxy config,
  • a Tiltfile that watches the wrong folder and rebuilds on every save,
  • a database that starts after the API that needs it,
  • a migration that runs inside the API's start script.

Each of these costs a debugging round. A smarter model makes fewer of them, so people paid for smarter models. The cost of that is real: more tokens, more waiting, and more review of output that is only infrastructure.

What I needed was not a smarter model. I needed the infrastructure to stop being a question.

What TDK CLI decides for you

TDK CLI treats each service as a typed resource. You declare what kind of thing you are building, and the generator fills in the rest.

The resource types are listed by tdk resource --help:

  • frontend: a Vite app that shares one Docker and nginx path with the other frontends.
  • backend: an API. Bun with Hono by default, or Express, Elysia, Fastify, NestJS, Koa, or h3. Go, Python, and Rust are available as language choices.
  • worker: a background process that runs without serving HTTP.
  • agent: a service that hosts an AI agent next to your other services.
  • mcp: a Model Context Protocol server, scaffolded with the SDK already wired in.
  • bring-your-own: a container you already have, registered so TDK can route to it.
  • sdk: a library rather than a running service.

Each type comes with decisions already made. A frontend gets an API client and an environment config. A backend gets health routes, a port read from the environment, and a binding to every interface, so it runs inside a container without extra work. An MCP service gets a server that creates a fresh instance per request, so there is no session to lose on restart.

The generated runtime files are then fixed by the tool, not by the model. For a frontend, that is 18 files under .autogenerated/, each marked as generated, and each checked against a saved snapshot before the stack starts. I counted them in a scratch project. The agent does not have to write them, and it cannot quietly drift from them without tdk up blocking the change.

What this changes for the agent

Once the infrastructure is decided, the agent's job narrows to three questions:

  1. What does this service do?
  2. What does it call, and what does it expose?
  3. Does the result work when tdk up starts it?

A less capable model can answer those. It does not need to remember which port the proxy expects, or which Dockerfile layer caches the dependencies. The tool answers those questions the same way every time.

The repo also writes some rules down where an agent will read them. The contributor guidance says new examples run database migrations in a separate migrator resource, not in the API's startup, and that tdk doctor warns when a backend runs a migration from its start script. That is one of the mistakes a capable model makes often, and the rule is there whether or not the agent remembers it.

TDK also exposes the stack to agents directly. tdk status --json reports readiness. tdk up --json reports ok: false with errors. tdk mcp gives an agent six tools (doctor, up, down, status, logs, resource_list), so it checks the result instead of trusting its own summary.

Three prompts for a simpler agent

These are the prompts I use. They are written for a model that is fast and cheap, not one that reasons deeply about infrastructure. Each one assumes TDK CLI is installed and the project was created with tdk project.

Prompt 1: a frontend that calls an API

Create a frontend resource called "shop-web" with tdk, using React.
Run: tdk resource shop-web --type frontend -y

Then open services/main/shop-web/src/App.tsx and build one page:
a product list that fetches from the API client in .autogenerated/api-client.ts
(do not write your own fetch URL). Show loading and error states.
Do not edit anything in .autogenerated/. Do not change service.json.
When done, run: tdk up main --dry-run and report what it says.
Enter fullscreen mode Exit fullscreen mode

Why it works: the agent has one file to change, one client to use, and a command to check the result. The rule against editing .autogenerated/ matches what the tool enforces anyway.

Prompt 2: a backend with a database

Create a backend resource called "orders" with tdk, using Hono.
Run: tdk resource orders --type backend -y

Add a GET /orders route that returns a JSON array. Keep the data in memory for now.
Add a GET /health route if one is not already there.
Do not change the port or the health path in service.json.
Then run: tdk up main --dry-run, and tell me which services it would start and in what order.
Enter fullscreen mode Exit fullscreen mode

Why it works: the agent builds the route and leaves the infrastructure alone. The dry run is a check the agent can read back.

Prompt 3: a worker that reads a queue

Create a worker resource called "order-emails" with tdk.
Run: tdk resource order-emails --type worker -y

Give it a loop that logs "processing" every five seconds.
Make it depend on the orders service by adding it to dependsOn in its service.json.
Then run: tdk up main --dry-run and confirm that orders starts before order-emails.
If the dry run shows any error, stop and report it instead of changing other files.
Enter fullscreen mode Exit fullscreen mode

Why it works: the dependency is declared, not hoped for. The agent checks the start order against what the tool reports, and it has a clear stop condition if something fails.

Note that the dry-run flag and the exact start-order output come from the CLI. Run the prompts once on your machine to confirm the output matches before you rely on them.

Where this stops working

This approach is not magic. I want to be clear about the limits.

  • TDK fixes infrastructure, not logic. A simple agent can still write a wrong price calculation or an insecure query. Review the application code the same way you would any code.
  • The results depend on the prompt. A vague request still produces vague code. The three prompts above are specific because the tool is specific.
  • You still need tests. The generated test file is a starting point. Write the checks that matter to your app.
  • TDK is for local development. Deployment stays with Helm, Argo CD, or Kustomize, and TDK does not generate cluster manifests by default.
  • Some apps need more than a simpler model. Hard algorithms, security-critical code, and unfamiliar domains still benefit from the most capable model. I use one for those.

Why I stopped reaching for the best model

Most of my time used to go into infrastructure review, not features. With TDK, that review mostly disappears, because the decisions are made by the generator and checked by tdk up. What is left is application code, and a simpler model does that well when the prompt is clear.

The savings are real. Smaller models cost less per token, respond faster, and make it easier to run several agents at once on different services. The tradeoff is that I review the application logic more closely, because I no longer assume the model got the hard parts right.

That is the whole argument. The tool carries the infrastructure, the prompt carries the feature, and I carry the judgment about the logic.

Try it

npm install -g @tdk-landscape/tdk-cli-core
tdk project --yes
tdk resource shop-web --type frontend -y
tdk up main --dry-run
Enter fullscreen mode Exit fullscreen mode

Then give your agent the first prompt above, and check the result against the dry run. If it works, the next two prompts follow the same pattern.

Top comments (0)