AI coding systems have made it possible to move from an idea to working software at a speed that would have sounded ridiculous a few years ago. Describe what the application should do, give the agent access to a project, and code starts appearing almost immediately. Files get created, libraries get added, interfaces take shape, tests run, and within a surprisingly short time, something appears on the screen that seems to work.
That speed is useful, but it creates an interesting security problem because code can now be produced faster than the decisions intended to control it. A request such as “build an internal security dashboard that lets analysts upload scan results and track findings” contains enough information to start generating an application, but nowhere near enough information to determine whether that application is secure. The missing details are not minor design choices either; many of them define the security boundary of the finished system.
Who can authenticate, what information each account can access, what an uploaded file is allowed to contain, where that file will be stored, what gets logged, how credentials are handled, and what happens when something fails are all unanswered questions. The same is true for network access, administrative privileges, record ownership, retention, external APIs, and deployment. If those decisions are missing when generation begins, the coding agent either has to stop and ask about them or fill in the blanks itself.
The second option tends to make for a much more impressive demo.
That is where the trouble starts, because a prompt is an instruction to an AI system. It is not automatically a requirement, a security policy, an architecture, or a definition of acceptable risk. A prompt can communicate all of those things, but only when someone has already made the decisions.
The Application Can Work and Still Be Wrong
One reason this problem is easy to miss is that insecure software does not necessarily look broken. A login page can work perfectly even when the underlying authorization is incomplete; a file upload can operate exactly as expected while allowing dangerous paths; and an API can return the correct information while quietly allowing one account to request records belonging to another. Nothing has to crash for the design to be wrong.
Consider an application that allows authenticated staff to retrieve security findings from an endpoint such as /api/findings/4281. The interface only displays findings assigned to the current account, so normal use looks completely reasonable, and every demonstration works as expected. If the backend never checks record ownership, however, changing 4281 to 4282 may return somebody else's data even though the interface never displayed a link to it.
The immediate reaction might be that the developer forgot authorization, but the failure often occurred earlier than that. The original requirement may have said only that authenticated users could retrieve findings, leaving the relationship between identity and record ownership undefined. The code then implemented exactly what was requested while failing to implement something nobody clearly required.
Secrets create the same kind of problem. An application needs to call an external API, so the agent needs credentials somewhere and creates configuration to support them. Unless secret handling has been defined, those credentials may wind up in source code, an environment file that gets committed, a browser bundle, a debugging message, or some other location that seemed convenient when the code was generated.
The flaw appears in the implementation, but the mistake began before the implementation existed. Nobody defined where secrets were allowed to live, who could access them, or how they would be delivered to the application. Once that distinction is understood, requirements stop looking like paperwork and start looking like one of the first security controls in the project.
A Prompt Describes What Is Wanted, Not Everything That Matters
A prompt can be several paragraphs long and still leave critical decisions unresolved. It may specify a programming language, database, interface, API style, and several features while throwing in words such as “secure,” “private,” or “production-ready” for good measure. Those words sound precise because everybody understands the general concept, but they do not define the behavior needed to produce it.
For example, “build a secure application that allows employees to upload documents” leaves many questions unanswered. A stronger requirement would state that authentication is mandatory; authorization is checked by the server for every upload operation; files are limited by type and size; filenames cannot control storage paths; files are stored outside executable application directories; and failed uploads are recorded without logging the contents of the documents. Now the application has actual behavior to implement rather than a general suggestion to behave itself.
The word “internal” creates the same ambiguity. An internal application might mean something listening only on localhost, something reachable from a corporate LAN, something exposed through a VPN, or something hosted in a cloud environment behind an identity provider. Those environments may all be described casually as internal, but their exposure, authentication requirements, monitoring needs, and likely attack paths differ significantly.
This is why asking an AI system to “make it secure” is roughly like telling a contractor to “build a secure house.” The contractor knows what a house is and can certainly put one together, but secure against what? A burglary, a fire, flooding, an earthquake, a power failure, or a teenager who keeps losing the front-door key all lead to different design choices.
The answer is not to tell the contractor where every nail should go. That would defeat the purpose of hiring someone capable of doing the construction. The answer is to define the important outcomes, boundaries, and unacceptable failures clearly enough that implementation decisions can vary without violating what actually matters.
From Idea to Something an Agent Can Safely Build
A useful progression is straightforward:
Each stage removes a different kind of uncertainty from the project. The idea establishes something worth building, while intent explains what the system is supposed to accomplish and why it exists. Requirements then convert that intent into behavior; constraints limit how the work can be performed; acceptance criteria establish how success will be verified; and the implementation task gives the coding agent a bounded piece of work.
An idea might be as simple as “create an application for tracking security findings.” That is perfectly adequate for brainstorming because it identifies a useful direction without pretending that design decisions have already been made. The problem begins when the same sentence is treated as sufficient authorization to generate an entire application.
Intent adds the missing purpose and boundaries. The application might be intended to allow authenticated members of a security team to review sanitized findings, record remediation notes, and track status while explicitly refusing to store scanner credentials, exploit payloads, unrestricted raw scanner exports, or unrelated operational data. No framework or database has to be chosen yet because the important question at this stage is what the system is supposed to be, not how the model happens to implement it.
Requirements take the next step by describing behavior. Authentication is mandatory; authorization is checked on the server; records are separated by approved group membership; administrative actions are recorded; and sensitive configuration is kept outside the source code. These statements can be understood by both the person directing the work and the AI system implementing it, which is exactly where they need to meet.
Constraints place limits around the solution. The agent may be prohibited from adding an external service without approval, changing dependencies without review, exposing the application to the Internet, modifying files outside the approved project directory, or sending telemetry to an external provider. Those boundaries matter because an AI agent can find perfectly reasonable technical solutions that are completely unacceptable in the environment where the software will actually run.
Acceptance criteria finally turn requirements into observable outcomes. Instead of saying that one account must not access another account's records, create two accounts, give one of them a record, authenticate as the other, and request that record directly by identifier. Access should fail, protected contents should remain undisclosed, and the failed request should create an appropriate security record.
Only after those decisions exist does the implementation task become useful. Rather than saying “build authorization,” the task can specify implementing server-side authorization for find/retrieve operations, adding negative authorization tests, modifying only the relevant route and associated tests, and stopping if the current data model requires an unapproved schema change. The model still has plenty of room to work, but it is operating inside a defined box instead of drawing the box while standing in it.
Constraints Matter Even More When the AI Can Act
There is another reason requirements have become more important: modern coding agents do considerably more than generate snippets. Depending on the environment, an agent may read the repository, create and delete files, execute shell commands, run tests, install packages, inspect configuration, and interact with other connected capabilities. That means a poorly defined request can influence more than code.
This changes the way permissions should be considered. If an agent is modifying one parser, it probably does not need access to deployment credentials, unrelated repositories, or production systems. If it is reviewing code, there is little reason to provide unrestricted command execution simply because that permission might become convenient later.
Giving an AI agent broad access because it might need something is a lot like giving every maintenance worker in a building the master key because walking back to the front desk would be annoying. It certainly saves time when someone unexpectedly needs another room. It also creates a much larger problem the moment the wrong door opens.
Prompts can help establish those boundaries, but prompts should not be confused with enforcement. Telling an agent not to change production is useful, while making production inaccessible to that agent is considerably stronger. Telling it not to access the network is helpful, while running the work inside an environment that cannot reach external systems is better.
The instruction communicates what is expected. The environment determines what is actually possible. Good AI-assisted security uses both rather than hoping polite wording can substitute for access control.
Acceptance Criteria Are Where “Secure” Becomes Measurable
Security language becomes useful when it survives an attempt to prove it wrong. “Users cannot access each other's records” sounds reasonable, but it becomes much more valuable when the requirement includes a repeatable way to test that claim. The same principle applies to file handling, authentication failures, secret exposure, logging, session management, API permissions, and nearly every other control.
Take file size restrictions, for example. “Limit uploads to 10 MB” leaves some important behavior undefined, including when rejection occurs, whether part of the file is written first, what happens to temporary data, and what the user sees when the limit is exceeded. A stronger criterion states that approved files up to 10 MB succeed; larger files are rejected before permanent storage; incomplete files are removed; the user receives a controlled error; and the failure is recorded without logging the file itself.
This is particularly valuable with AI-generated code because the code often looks convincing even when something subtle is wrong. Function names make sense, comments sound authoritative, automated tests may pass, and the model can provide a wonderfully confident explanation for why everything is correct. Unfortunately, confidence has never been an access-control mechanism.
The application has to demonstrate that the control works under the conditions that matter. That means normal tests, negative tests, malformed input, unauthorized requests, unexpected state, and failures in supporting components all deserve attention. Security becomes much easier to reason about once the requirement defines what failure should look like instead of merely insisting that failure should not happen.
The Secure Build Brief Does Not Need to Become a Bureaucratic Monster
None of this requires producing a forty-page specification before asking an AI system to write a small utility. That would throw away much of the speed that makes AI-assisted development useful in the first place. A good secure build brief can be short as long as it answers the questions that would otherwise become dangerous assumptions.
For a modest application, the brief should establish the problem being solved, approved users, allowed actions, data being processed, prohibited data, deployment location, authentication expectations, authorization boundaries, secret handling, logging, dependency restrictions, forbidden behavior, release conditions, expected failure behavior, and rollback expectations. That sounds like a lot until it is written down, at which point most of it fits comfortably into a page or two. The value comes from making decisions visible before they turn into code.
The AI can help improve that brief as well. It can look for ambiguity, identify missing trust boundaries, suggest negative tests, challenge assumptions, and point out areas where the requirement still gives the implementation too much freedom. That is a much better use of the model than asking it to silently invent the security architecture and hoping the result lines up with expectations.
There is an important difference between asking the AI to make security decisions and asking it to identify security decisions that have not yet been made. The first transfers authority without enough context, while the second uses the model as a very fast second set of eyes. That distinction preserves the speed of AI-assisted development without pretending the model somehow knows organizational risk that nobody has explained to it.
The Point Is Still to Move Faster
Security requirements are sometimes treated as something that slows development down. In practice, vague requirements often create the opposite of speed because the missing decisions return later as rewrites, dependency changes, authorization repairs, configuration cleanup, broken assumptions, and the occasional unpleasant discovery that a credential has been sitting in source control. The project looked fast because the expensive part had simply been postponed.
Five minutes spent defining data ownership can prevent the need for an authorization redesign. A dependency restriction can prevent several unnecessary libraries from getting embedded throughout an application, while a clear non-goal can stop an internal utility from quietly becoming an Internet-connected service because an external API happened to make one feature easier. A little friction at the beginning is cheap compared with removing the results of an incorrect assumption after several thousand lines of generated code depend on it.
The strongest AI-assisted workflow is not the one that produces the most code from the shortest prompt. It is the one that moves from an idea to working software without losing control of what the system is supposed to protect, what the agent is allowed to change, and how anybody will know whether the finished result actually meets the requirement. Speed is still the advantage, but now the speed has direction.
A prompt starts the conversation with the machine, and that is an extremely powerful place to begin. The requirements define the system the machine is supposed to build, while the constraints and acceptance criteria keep that system within boundaries that can be checked rather than merely assumed. Once those pieces are in place, the AI can do what it does best: move very quickly without being asked to guess which mistakes matter.

Comments
Post a Comment