AI Should Stop Generating the Same Code Over and Over
AI coding tools have become ridiculously good...guess we can all agree. You can describe an application, wait a few minutes, and suddenly you have React components, API endpoints, database models, authentication, migrations and a decent-looking application...it seems to be magically. Then you dig into the code, try to change something and something you didn't even remotely touch breaks. Then you spend another few thousand tokens explaining the architecture the AI itself created 30 minutes ago. I was sick of it.... A new way to build business apps As part of a platform for small businesses I'm currently working on I am also experimenting with a different architecture for AI-built business software: let AI describe the application, then compile the repeatable parts deterministically especially do not have the AI build things like authentication, connectivity, database stuff...you name it. That has all been solved for 20 years and we shouldn't ask AI to build it over and over again, failing over and over again at getting things right. The AI only has to write YAML using my own vocabulary. Think about something like this: entity: expense_claim fields: submitted_by: type: reference targetEntity: person amount: type: money currency: EUR required: true or this..a little bit more involved: entity: assumption ownedBy: parent: scenario via: scenario as: table display: statement fields: statement: label: Assumption type: text required: true scenario: label: Scenario type: reference required: true indexed: true targetEntity: scenario onDelete: cascade And this isn't limited to entities or CRUD. Automations can be described in the same vocabulary: automation: build_segment_grid name: Lay out a segment's lifecycle effects: - type: createForEach set: scenario: '{{record.scenario}}' segment: '{{record.id}}' point: '{{source.id}}' label: '{{record.name}} {{source.label}}' flex: pick: entity: revenue_plan filters: - field: scenario operator: eq value: '{{record.scenario}}' - field: tier operator: eq value: flex sort: - field: name direction: asc We can describe the whole application with YAML definitions, from entities, relations, workflows, automations, permissions up to views and screens and readymade widgets...its all in YAML. Yet, The exact YAML isn't really the important part. It could be JSON. It could eventually be authored visually. Most of the time it may simply be written and modified by an AI. The important part is the level of abstraction. The definition says what the business application should do. It doesn't describe how ASP.NET should register a controller, how EF Core should configure a relationship, how Vue should render the form or how an MCP tool should expose the operation, and ASP.NET is just an example here I use as thats the first target compiler that exists today. More to come. Yet...those things can be derived. So...isn't this just another code generator? And hey, YAML rings a bell. Someone must have built something like this before. Absolutely! Lowdefy, for example, takes a similar approach in some ways: you describe an application using YAML instead of manually writing all the implementation code. But there is one important architectural difference. Lowdefy's YAML describes an application for the Lowdefy runtime. The runtime interprets that configuration and provides the UI, data connections, actions, authentication and other application machinery. That is a perfectly valid architecture, but your application is fundamentally expressed in terms of that runtime. My approach is different and goes way beyond that! Compiling actual code from YAML For my project theres two options...The first is to run the application on the Cordango platform. The definition is compiled into an internal JSON representation which the platform uses to build and run the app. The platform adds some nice things on top, like apps sharing company data and reacting to each other's events without somebody having to manually build an integration between every single tool. But that's not really what I want to talk about here. The second path is the more interesting one for this article: What if that YAML is compiled into actual source code? Not a starter template. Not a few generated models you still have to wire together. An actual standalone application with a frontend, backend, database model, migrations, authentication, permissions, business logic, REST/OpenAPI and MCP. Something you can take, build, run and host yourself. The current architecture looks roughly like this: App Definition | v Deterministic Compiler | +----------+----------+ | | v v Frontend Backend Vue / Vuetify ASP.NET Core | | | +----------+----------+ | | | | | REST/API MCP Commands | OpenAPI Workflows | | Permissions | | Calculations | | +----------+----------+ | EF Core | Database Model Migrations | PostgreSQL Today the first complete target generates ASP.NET Core + EF Core + PostgreSQL + Vue 3/Vuetify but Node/Typescript is on its way, Python is being planned and more to come. And because all of those pieces come from the same application definition, they don't have to be separately invented and kept in sync. If the definition says an expense_claim exists, that isn't just a database table. The compiler can derive: the entity model persistence migrations CRUD operations permissions forms and views REST/OpenAPI MCP access commands and workflows Change the definition and the generated implementation changes with it. The YAML isn't configuration for the generated application. It is the source from which the application is compiled. And this leads to one of my favorite slightly ridiculous consequences: You could literally gitignore the code I'm serious. If your App Definition is stored in Git and you pin the compiler/generator versions required to build it, the generated source becomes a build artifact. Given the same application definition, generator version and scaffold version, the compiler generates the same files again. So technically your repository could look roughly like this: my-app/ ├── cordango.yaml ├── apps/ │ └── expenses/ │ ├── app.cordango.yaml │ ├── entities/ │ ├── roles/ │ ├── views/ │ └── workflows/ └── .gitignore while this: generated/ ├── api/ ├── web/ ├── Dockerfile └── docker-compose.yml could simply be regenerated whenever you need it. Now, whether you should gitignore all generated source is a different discussion. Keeping generated code in Git can obviously be useful for reviewing changes, deployments and debugging. But architecturally, you don't have to treat it as the source of truth anymore. That's the important part. The definition is the source. The code is an output. And unlike a traditional runtime-based no-code platform, taking that output doesn't mean taking a dependency on my hosted service. There is no Cordango account requirement, licence server, model API or phone-home service involved in running the generated application. The current standalone runtime is small enough that it can either be referenced as a package or embedded into the generated project itself. So you can generate the application, take the source, and host it wherever you want. An AI should reason about what you want to achieve not fundamentals With this approach, the AI gets more headroom to reason about the actual business problem instead of spending context and tokens on code we've already solved a thousand times. It also has far less room to accidentally break unrelated implementation details. If it changes a workflow, it changes the workflow definition. The compiler handles the rest. The AI can still misunderstand the requirement, of course. But those are business-level mistakes, not random implementation drift. And it doesn't even have to be a coding model...I literarily build one of the sample apps with ChatGPT...yes the old standard one, not codex. A nice added extra...it saves a ton of token I don't want to make this the main part but yes, using YAML saved a ton of token. I compared the generated examples vs the YAML definition with Anthropic's tokenizer and the result was up to 98% fewer token consumed...lets see how that adds up when more work is done but it definitely saves you..some huge chunk. What now? How does this work? YAML alone doesn't solve much if the AI still has to read an entire repository every time something changes. So Cordango comes with a CLI around the application model. The basic loop becomes: Describe a change ↓ AI changes the relevant definition ↓ cordango check ↓ cordango build ↓ Deterministically generated application The AI works on the small semantic representation, not hundreds of generated controllers, migrations, components and API files, the CLi gives the AI the tools it needs to do that. And because the compiler understands the vocabulary, it can reject invalid definitions before they ever turn into source code. cordango check cordango build --target app --generator dotnet-vue That's really the whole bet. Want to have a look? https://www.github.com/cordango https://docs.cordango.com https://www.cordango.com All feedback welcome, negative, positive, roast it, praise it...give it a try! make the thing AI has to understand smaller, more meaningful and harder to accidentally break.
This is a summary aggregated from Dev.to. Read the complete article on the original site:
Read full article at Dev.to