Why AI Won't Build Secure Software Unless You Instruct It How
One security requirement in a planning document changed an entire database design. A field note on engineering requirements driving AI-assisted development.
TL;DR
- AI only optimizes for the context you provide. No security context means default-grade design.
- One line in a planning document — "secure development practices, zero package vulnerabilities, no
audit fix --force, pin versions" — changed table design, RLS structure, function privileges, and grants. - Non-functional requirements are engineering constraints. Write them down before the first line of code is generated.
- Ask the AI what it would have done without your constraints. That question exposes invisible assumptions.
How one security requirement changed an entire database design
While building one of my recent full-stack applications using Lovable, I came across an interesting reminder about AI-assisted software development.
The application itself was straightforward. A user registration form. Two user roles. A Supabase backend. Nothing particularly complex.
However, before asking Lovable to implement the feature, I added one section to my planning document.
Use secure development practices with zero vulnerabilities in packages.
Don't run audit fix force.
Add version pin for safe packages.
At first glance, this looks like a small instruction. In reality, it changed the way the AI designed the application.
AI builds from context, not assumptions
After the implementation was complete, I asked Lovable a simple question.
How did my earlier security prompt influence your implementation? What would you have done otherwise?
Its response was surprisingly transparent. Without my security guidance, it said it would likely have:
- Created the
profilestable and stored user roles directly on it, because that appears to be the simplest design. - Written basic Row-Level Security (RLS) policies using
auth.uid() = user_idwithout considering administrator access patterns. - Forgotten explicit
GRANTstatements after creating database tables, leading to permission failures at runtime. - Left helper functions executable by the
publicrole, creating unnecessary attack surface.
Those choices would probably still produce a working application. But they also introduce architectural and security weaknesses that are easy to overlook during rapid AI-assisted development.
What changed because of one security requirement
Lovable explained that my security context changed several design decisions.
Instead of storing roles directly in the user profile, it separated authorization into a dedicated user_roles table. Instead of relying on simple ownership checks, it introduced a dedicated has_role security-definer function.
It also followed a consistent sequence for every new table:
1. CREATE TABLE
2. GRANT permissions
3. Enable Row-Level Security
4. Define security policies
And it revoked public execution permissions from internal helper functions rather than leaving them accessible by default.
These aren't small improvements. They fundamentally changed the security architecture of the application.
Why doesn't AI simply do this automatically?
I was curious about that too, so I asked another question.
Why do you expect users to explicitly specify security requirements instead of incorporating them automatically?
The answer highlights something every AI-assisted developer should understand.
1. Security depends on context
A public landing page and a healthcare application do not require the same security controls. Without understanding the application's threat model, regulatory requirements, or business risk, an AI cannot safely assume the appropriate level of protection.
2. Security always involves trade-offs
Email verification, strict RLS, manual approval workflows, role separation, and rate limiting all affect user experience. A model shouldn't decide those trade-offs on your behalf without being instructed.
3. AI defaults to a baseline
Lovable explained that it generally implements common security practices, but intentionally avoids assuming advanced requirements — role-based authorization, audit logging, separation of duties, fine-grained privilege models — unless explicitly requested.
4. Requirements become engineering constraints
Perhaps the most interesting statement was this:
"Your prompt acted as a constraint. When you explicitly said 'follow these DB security rules,' it overrode my defaults and made me check things I might otherwise have skipped."
That's the key lesson. AI tools aren't simply following prompts. They are operating within engineering constraints.
This isn't simply about security
The same principle applies to almost every aspect of software engineering. Without explicit guidance, AI may not consider:
- Performance objectives
- Scalability requirements
- Compliance obligations
- Observability
- Deployment strategy
- Backup and disaster recovery
- Cost optimization
- Multi-tenancy
- Maintainability
- Coding standards
Not because the AI is incapable — because those are project-specific engineering decisions.
Best practices I followed
Start with a planning document
Before generating code, define important architectural and engineering constraints. Treat the planning document as part of the project, not as an optional prompt. This is the same durable-context discipline described in How to Avoid Context Burnout in AI Coding Workflows.
State non-functional requirements explicitly
Don't just describe what the application should do. Also describe how it should behave: security expectations, performance goals, deployment model, package management rules, authentication requirements, database conventions.
Define engineering conventions
Rather than asking AI to "build a signup page," explain the standards the project should follow — version pinning, secure package management, least-privilege access, database design conventions, repository structure. The pinning rules I use are in Secure Package Version Pinning for npm, Bun, and pnpm.
Ask AI to explain its decisions
One of the most valuable parts of this exercise wasn't the generated code. It was asking: "What would you have done differently without these requirements?" That question exposed implementation assumptions that would otherwise remain invisible.
Review the architecture, not just the code
Instead of checking whether the code compiles, ask:
- Why was this table designed this way?
- Why is this permission required?
- Could privilege escalation occur?
- What happens if another role is introduced later?
These questions reveal architectural issues that syntax reviews won't catch. They belong in the review layer described in Why Process-First SDLC Matters More in the AI Coding Era.
Closing thought
AI-assisted development isn't just about writing better prompts. It's about providing better engineering context.
The more clearly you communicate your architecture, security expectations, operational constraints, and coding standards, the more likely the generated solution will align with the software you actually want to build.
In my experience, the quality of AI-generated software is rarely limited by the model itself. More often, it's limited by the quality of the engineering requirements we provide before the first line of code is generated.
Ask AI About the Author
Open this query in ChatGPT, Claude, or Perplexity.
Comments
Comments are open to confirmed email subscribers. Use the email you subscribed with. To edit a comment, delete it and post a new one.
Subscribe to get the new blogs.
Field notes from someone who ships before they write about it. Sovereign AI, AI-SDLC, DevOps, and what 59 production deployments teach you. No spam. Unsubscribe anytime.