The Problem With Being Able to Build Anything
AI lowers the cost of trying an idea. The harder work is deciding which idea deserves it, designing for the consequences, and knowing when to stop.

I have a habit of researching before I build. If I am working on an internal tool, an app, or a creative project, I want to understand the problem before I start choosing tools. Sometimes that habit becomes overthinking. I can spend too long comparing options that would probably all work. But the opposite mistake is much easier to make now: building something quickly, then discovering that I never understood why it needed to exist.
AI has changed the distance between an idea and a first version. I can ask for an interface, generate a script, explore a visual direction, wire together services, or get a prototype running without first mastering every part of the stack. The result may need substantial correction, but the first attempt is cheaper than it used to be.
That sounds like an uncomplicated win. Often, it is. The problem is what happens after the first version arrives. Once something exists, even as a rough prototype, it becomes easier to justify spending more time on it. The interface is there, so perhaps it needs a dashboard. The dashboard is there, so perhaps it needs analytics. A new feature is only another prompt away, so why not add it?
A thing being buildable and a thing being worth building are two different questions.
The cost did not disappear. It moved.
I do not think execution has become free. Real systems still need debugging, integration, testing, deployment, security, and maintenance. Generated code can be wrong in ways that are hard to see. A tool can finish a task quickly and leave behind an implementation that costs more to understand than it saved to write. What has become cheaper is the attempt: the rough implementation, the extra variation, the experiment that used to require enough effort to make you think twice.
That changes the economics of trying things. We can explore more options before committing to one. We can prototype an assumption and show it to someone. We can throw away a version without losing a week. Those are meaningful advantages. But every option still demands attention, and every implementation that survives the experiment becomes something to evaluate, maintain, or remove.
The bottleneck does not move to one neat replacement. It spreads across deciding what to try, giving the system the right context, judging the result, integrating changes, and taking responsibility for what ships. Sometimes code is still the slow part. Sometimes the slow part is understanding a legacy workflow. Sometimes it is waiting for users to tell you whether the thing is useful. AI does not remove those differences.
A useful caution comes from METR's early-2025 randomized study of 16 experienced open-source developers working on 246 tasks in repositories they knew well. In that particular setting, developers using the available AI tools took 19% longer to finish. Before the tasks, they expected a speedup; afterward, they still felt faster than the measured result suggested. The study was not a verdict on every model, developer, or kind of task. It was a reminder that perceived speed and completed work are not the same measurement. [1]
The point is not that AI makes developers slower. The point is that productivity depends on the task, the codebase, the tools, and the work required to check the answer. A fresh prototype with a clear success condition is different from changing a mature system whose rules have accumulated over years. In the first case, generation may remove a lot of friction. In the second, a model can produce plausible changes while missing the reasons the existing system looks the way it does.
Before the stack, there is the problem
Imagine building an HRMS for a small company. The feature list writes itself: attendance, leave, task assignments, salary information, dashboards, reports. An AI tool can help generate screens for all of them. But a feature list is not yet a product specification. It says what the system might contain, not what people need it to do.
Do all departments follow the same working hours? Can someone start a task before checking in? Who can edit an attendance record after it has been approved? Does work-from-home eligibility depend on a role, a monthly allowance, or a manager's approval? What happens when the schedule changes halfway through a month? When someone requests a dashboard, what decision are they trying to make with it?
These are not tiny details to polish after the interface is done. They determine the data model, permissions, audit history, notifications, and what the system considers a valid action. They also reveal where the system should be configurable and where flexibility would be dangerous. If two departments genuinely work differently, hard-coding one schedule creates friction. If everyone can casually redefine payroll rules, flexibility becomes a risk.
Only after the workflow is understood can architecture be judged properly. Is a hosted backend enough, or does the data require tighter control? Is a managed database worth the monthly cost? Would a self-hosted service save money but create a maintenance burden? Does the company need multi-office support now, or is that an abstraction for a future that may never arrive? What needs to scale: traffic, the number of employees, the number of workflows, or simply the number of people who can safely maintain the code?
There may be several reasonable stacks. The useful choice is the one that fits the real constraints, including the people who will operate it. A technically elegant system that the company cannot afford to host or maintain is not elegant in context. Neither is the cheapest option if it creates a security risk or makes every future change expensive. Architecture is a series of trade-offs, not a leaderboard.
Research is how I try to make those trade-offs visible before they become expensive. It can also turn into its own form of avoidance. I can compare providers, frameworks, and deployment patterns long after the remaining differences no longer affect the outcome. The point of research is not to eliminate uncertainty. It is to reduce the uncertainty that matters enough to choose a path, test it, and learn.
An experiment needs an exit
AI makes it very easy to continue exploring. One more architecture, one more design direction, one more agent, another round of feature ideas. Exploration feels productive because each step produces an artifact. But output is not necessarily evidence, and a larger collection of options can make the decision harder rather than easier.
I think research needs an exit condition just as much as implementation does. Before a technical experiment, define what you are trying to learn. Before comparing services, identify the requirements that would make one unsuitable. Before adding a feature, state which user problem it is supposed to reduce. If every result creates three new questions and none of them changes the decision, the research loop is no longer paying for itself.
There is a difference between uncertainty that matters and uncertainty that can be left alone for now. If a choice is reversible, cheap to test, and has limited consequences, it may be better to make a reasonable decision and move. If it affects data ownership, security, a core data model, or an expensive migration, more investigation can be justified. Treating every decision as equally important wastes time; treating every decision as reversible creates avoidable damage.
What does finished mean?
This is where I find the most practical version of the problem. If I can keep building almost indefinitely, what tells me to stop? A feature can always be expanded. The interface can always be refined. A workflow can always be made more configurable. Without a definition of enough, cheap implementation encourages endless expansion.
For a product, I want to know that the core user problem is being addressed, that the people who will use it can complete the important tasks, and that the result has been tested beyond the ideal path. Security and correctness requirements need to be met. Known limitations should be written down rather than quietly ignored. There should be some evidence that the product helps,not just that the generated interface looks convincing.
A practical stopping check: the core problem is addressed; important flows have been tested; security and correctness risks are understood; users can complete the work they came for; the remaining gaps are explicit; and any additional feature has a reason beyond being easy to generate.
That is not an argument for shipping a half-finished product and calling it minimal. The stopping point depends on the consequences of failure. A personal prototype can tolerate rough edges that an attendance, payroll, or medical system cannot. A low-risk idea can be tested quickly; a destructive action needs stronger safeguards. “Enough” is contextual, but it should not be undefined.
Sometimes the best outcome of a prototype is learning that the idea is wrong. Sometimes the right solution is a spreadsheet and a small script, not a platform. Sometimes a feature should remain a note for later because nobody has shown that they need it. Leaving something out can be a design decision rather than a failure of ambition.
The feature has a second price
The cost of a feature is not just the time it takes to make it work once. It includes the cost of owning it: handling empty states, edge cases, permissions, retries, failures, documentation, support, monitoring, and the interactions it introduces with everything already there. An extra field may require validation in three places. A new role may change what several screens and API calls are allowed to do. A setting that sounds small can create a second behaviour the team must test forever.
A generated feature can pass a happy-path demo and still be unfinished. What happens when two people edit the same record? Does the permission check happen on the server or only in the interface? What happens when a network request succeeds but the client times out before receiving the response? Can the system recover from a partial failure? Will a later developer know which part of the behaviour is intentional?
This is why “it runs” is a weak definition of done. The system has to behave correctly when conditions are less convenient than the demo. The exact standard depends on the use case, but there should be a standard. If AI reduces the cost of producing code while the team leaves verification unchanged, it can simply push the bottleneck into code review, bug fixing, and maintenance.
Fast code can still become slow software
There is a style of AI-assisted development that resembles fast food: prompt, generate, run, patch, move on. For throwaway exploration, that is often reasonable. The trouble begins when temporary code becomes the permanent foundation without anyone reviewing the structure it is creating.
The repository may grow faster than the team's understanding of it. Responsibilities end up scattered across components. Similar logic is copied instead of shared. Data access becomes entangled with interface code. Error handling changes from one screen to another. Tests check whether the latest demo works but not whether a business rule still holds. Each individual patch can look sensible while the overall design becomes harder to reason about.
The answer is not to create an elaborate architecture for every small application. Clean code does not mean the shortest code, the most abstractions, or the largest folder structure. It means the code has understandable responsibilities and boundaries that fit the expected changes. A monolith can be a good design. Splitting a small product into services can be a very expensive way to avoid making a simpler decision.
Good design is partly the work of anticipating change. What is likely to change? What should remain stable? Which parts need to know about each other, and which should be able to move independently? What can be tested without running the whole application? These are decisions about the future cost of modification. AI can implement them, but it needs them to be made,or it needs a clear process for proposing and validating them.
Preserving the reason
One idea I keep returning to is a file called GUARDRAILS.md. Not another giant manual, and not a place to dump every detail of the project. I imagine it as a compact record of the things a future contributor or agent must know before making a change: important constraints, assumptions, architectural boundaries, intentional exceptions, risky areas, and changes that need a second look.
The filename is not the invention. Architecture Decision Records already capture why a significant technical choice was made, what alternatives were considered, and what consequences follow. Repository instructions such as GitHub Copilot's custom-instructions files and AGENTS.md can give coding tools project-specific rules. These are existing practices, and they solve overlapping but different problems. [2][3]
I would separate them by purpose. Agent instructions can explain how to work in the repository: commands, conventions, test steps, and where things live. An ADR can preserve a consequential decision: the context, the choice, alternatives, and trade-offs. A guardrails document could surface the cross-cutting invariants that a contributor should not casually violate, such as where permissions must be enforced, which data is sensitive, or which migrations require approval. It should point to the detailed source of truth rather than duplicate everything.
The crucial part is the why. Code can show what happens. Tests can encode expected behaviour. Documentation can explain how to operate a system. But why a strange-looking constraint exists can be much harder to infer. Perhaps a field is duplicated because an external integration requires a specific shape. Perhaps a permission check exists in two layers for a deliberate reason. Perhaps a simplification was rejected because it broke offline use. Without the rationale, the next person,or agent,may remove the guardrail because it looks redundant.
The inverse matters too. A decision that was right six months ago can become wrong. Documentation should preserve reasoning, not freeze the project in time. A useful record includes its status and can be superseded when new evidence changes the trade-off. Otherwise, the instruction file becomes a museum of assumptions that nobody dares to question.
Context is not just a bigger prompt
An AI agent does not work with the project as a human teammate might. It works with the context made available to it: the files it reads, instructions it receives, tools it can use, state it can observe, and results returned by tests. That surrounding context can determine whether the model is useful. A capable model with incomplete or stale context can confidently make the wrong change; a better-scoped task with the right files and a reliable check can make a less ambitious model useful.
This is what people mean by context engineering, though the phrase can sound more mysterious than the work itself. It includes deciding what information matters for the current task, where that information lives, how to retrieve it, what should be persistent, and what should be left out. More context is not automatically better. Repeating every convention in several files creates drift. Loading a long history can bury the one constraint that matters. A note that no longer reflects the code can be worse than no note because it gives the agent a reason to be wrong.
A useful context system is selective and maintained. It gives an agent the task, the relevant implementation, the constraints that affect the decision, and a way to verify its work. It also distinguishes an established rule from a guess. If the model does not know whether a behaviour is intentional, the right response may be to ask or mark the assumption,not invent certainty. [4]
More agents do not automatically mean more progress
Once one agent can implement a feature, it is tempting to add more: one for research, one for coding, one for review, another to check the reviewer. Parallel work can help when the tasks are genuinely separable. It can also multiply the amount of work that has to be reconciled.
Two agents can choose incompatible data models. A research agent can recommend a library that a coding agent never sees the constraints for. One agent can rename an interface while another builds against the old name. A reviewer can spot a stylistic issue and miss a flawed business assumption. Even when every agent does its assigned job well, the combined result can be inconsistent because the shared decisions were never made explicit.
The solution is not automatically another model that supervises all the others. That layer adds cost, latency, and a further chance of confident error. A deterministic test is better for a rule that can be expressed as a test. A type checker is better for a type error. A permission boundary is better than asking an agent to remember not to cross it. A separate reviewer is useful when it has a clear review scope, enough context, and a way to report evidence rather than just give a verdict.
Orchestration is system design too. Work has to be split at useful boundaries; agents need compatible assumptions; the interfaces between their tasks need to be explicit; and somebody or something has to integrate the results. Parallelism is valuable when it reduces the critical path without creating an even larger review and coordination burden. Otherwise, it has made the work arrive faster without making it smaller.
Who should be allowed to decide?
It is easy to say that humans should decide and agents should execute. It is a useful starting point, but too broad to be a system design rule. Many decisions are routine, low-risk, and reversible. Others affect privacy, money, access, production data, or a core data model. Treating all of them alike either creates needless approval queues or gives automation too much authority.
I find it more useful to think in terms of consequence and reversibility. An agent can usually be trusted to format a file or draft a test with little ceremony. A schema migration needs a plan and validation. Deleting records, changing payroll logic, or altering who can access sensitive information requires stronger controls. The less reversible the action and the greater its potential harm, the more evidence and oversight it should require.
That oversight does not have to mean a person manually inspecting every line. It can be a combination of least-privilege permissions, automated tests, staged deployment, audit logs, rollback paths, and explicit approval for certain actions. Humans remain responsible for the policy and the risk boundary. Agents can act autonomously inside that boundary when the task and the evidence justify it.
Technical skill still matters
None of this makes coding irrelevant. Technical knowledge is what lets you recognise when an implementation is subtly unsafe, when an abstraction will spread complexity instead of containing it, when a test proves too little, or when an agent has solved the visible problem while violating an important constraint. You do not need to personally type every line to be responsible for a system. You do need to understand enough to evaluate what is being shipped.
Expertise is not only the ability to perform a task. It is also knowing which details matter, spotting an implausible result, understanding why a shortcut is dangerous, and deciding what evidence would be convincing. AI changes the balance between producing an attempt and judging it. It does not make those forms of knowledge interchangeable.
The same thing happens outside software. A creative model can generate many visual directions, but a person still has to decide which one serves the brief, which details are culturally or emotionally wrong, and which version has the right restraint. A research assistant can collect sources without producing a useful argument. More output gives you more material to work with; it does not automatically produce a better result.
Measure the result, not the activity
If a team measures AI productivity by lines of code, number of generated features, or how many tasks an agent claims to have completed, it will encourage exactly the wrong behaviour. Those numbers reward output even when the output needs to be discarded or creates future maintenance work.
Better measurements depend on the work, but they should follow it through to an outcome: time to a verified change, user success, escaped defects, rollback frequency, review effort, maintenance burden, or whether the problem is now less painful. A prototype that disproves an assumption can be valuable even if none of its code ships. A hundred generated components can be waste if the original workflow remains confusing.
This is the counterpoint to the risk. Cheaper execution can improve judgment when it makes useful experiments cheap. You can test a workflow with real users before committing to a major build. You can compare two architectures with a small spike rather than arguing from preference. You can generate a visual mock-up to discover that the brief itself is wrong. The advantage is not merely making more things; it is learning sooner which things are worth making.
The question that remains
I do not think the right response is to resist building quickly. It would be strange to ignore tools that let us explore an idea, test a constraint, or create something that used to be out of reach. The useful response is to become more deliberate about what happens around the generation: framing the problem, choosing constraints, keeping the context current, checking the result, and deciding what not to add.
The skill is not to find the perfect stack, write the longest instruction file, or create an agent for every task. It is to match the amount of thought and control to the decision. Investigate what is consequential. Move quickly when the choice is cheap and reversible. Record the reasons that a future change could otherwise erase. Test what can be tested. Ask for evidence before adding complexity. Stop when the problem has been addressed well enough for the stakes involved.
There will always be another feature to add, another framework to try, another output to generate. The fact that an idea is now within reach does not make it important. And making something quickly does not mean its consequences will be quick to deal with.
The problem with being able to build anything is not that we will run out of ideas. It is that we can spend less and less time deciding which ones deserve to become systems, products, or commitments,and then spend a long time maintaining the things we built before we asked.
Further reading
[1] METR , Measuring the Impact of Early-2025 AI on Experienced Open-Source Developer Productivity: https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study/
[2] AWS Prescriptive Guidance , Architectural Decision Record process: https://docs.aws.amazon.com/prescriptive-guidance/latest/architectural-decision-records/adr-process.html
[3] GitHub Docs , Adding repository custom instructions for GitHub Copilot: https://docs.github.com/en/copilot/how-tos/copilot-on-github/customize-copilot/add-custom-instructions/add-repository-instructions
[4] Rahul Garg / Martin Fowler , Context Anchoring: https://martinfowler.com/articles/reduce-friction-ai/context-anchoring.html
Context Engineering for AI Agents in Open-Source Software , arXiv: https://arxiv.org/abs/2510.21413