Eduard Tamsa

Eduard Tamsa

Software thinkerer

All articles

AI Coding Tools Need Constraints, Not Just Prompts

A detailed prompt can explain what you want. It cannot reliably define everything the AI must not do.

That is why good AI-assisted development needs constraints outside the prompt.

Teams often spend time improving instructions: add more context, describe the architecture, request a smaller diff, ask the tool to be careful. Better prompts help. But a prompt is still a request interpreted by a probabilistic system. It is not a permission boundary, a test suite, or an enforcement mechanism.

If a rule matters, encode it somewhere the tool cannot politely misunderstand.

Prompts Are Context, Not Control

A prompt is useful for explaining the goal, the relevant code, and the tradeoffs. It gives the AI enough information to choose a reasonable implementation.

The problem begins when teams use text instructions as the only safety mechanism.

“Do not change the authentication flow.” “Do not touch production configuration.” “Preserve backward compatibility.” “Only update this module.”

These are good instructions, but they need support. A broad coding agent may still find a path through another file. A generated change may alter shared behavior indirectly. A dependency upgrade may change a contract without editing the protected module at all.

Important constraints should exist in permissions, tests, repository rules, interfaces, and automated checks.

The prompt explains intent. The system enforces boundaries.

Limit the Writable Surface

One of the simplest controls is limiting where the tool can make changes.

If the task is to add a Jekyll post, the agent does not need write access to deployment credentials, infrastructure repositories, or unrelated applications. If the task is to update a Terraform module, it may need to read consumers but should not automatically modify every consumer.

Smaller write boundaries reduce both mistakes and review effort.

This can be implemented through sandboxed workspaces, scoped repository access, branch protections, path ownership, or simply running the tool in a checkout that contains only the relevant project.

The principle is the same one used for production systems: give the process the minimum access required for the job.

AI does not become more useful because it can modify everything.

Use Tests as Executable Constraints

Tests define behavior more clearly than repeated reminders in a prompt.

If backward compatibility matters, cover the existing contract. If a permission rule must remain intact, test allowed and denied cases. If an automation must be idempotent, run it twice and verify that the second execution has no unintended effect.

Then require the relevant suite to pass before accepting the change.

This moves the conversation from “the AI says it preserved behavior” to evidence that the expected cases still work.

Tests are not complete protection. They only enforce what they cover. But they provide a stable boundary across many prompts, tools, and contributors.

The best constraint is one that remains active after the conversation ends.

Protect Irreversible Operations

Coding tools increasingly do more than edit files. They can run commands, change cloud resources, publish packages, open pull requests, and deploy applications.

Those capabilities need clear approval boundaries.

An agent may be allowed to inspect a production incident, gather logs, and prepare a rollback. Executing the rollback is a separate decision. It may draft a database migration, but applying it to production should require a controlled pipeline. It may update a package version, but publishing the release should happen through protected automation.

Separate preparation from execution.

This creates a reviewable checkpoint before an external or irreversible action. The human can inspect the diff, test results, affected systems, and recovery plan.

A sentence in a prompt is not enough protection for a command that can delete data.

Constrain the Size of Change

Large AI-generated diffs are difficult to review because plausibility scales faster than understanding.

A tool can create consistent naming, clean abstractions, and complete-looking documentation across many files. That visual consistency makes it tempting to accept the whole change. The reviewer still has to verify whether every behavior is correct.

Set a smaller unit of work.

Ask for one behavior, one module, or one migration step. Require the tool to preserve existing patterns. Keep formatting and unrelated cleanup out of functional changes. Use checkpoints before broad refactors.

When a change must be large, divide it into stages that remain valid on their own: add tests, introduce a compatibility layer, migrate one consumer, then remove the old path later.

Small changes are not only easier to review. They are easier to revert and easier to diagnose when a test fails.

Make Architecture Visible

AI tools cannot respect boundaries they cannot see.

Many repositories rely on unwritten rules: this package owns persistence, that module must not import from the API layer, production configuration changes require a particular review, and public interfaces need a deprecation period.

Some of this belongs in concise repository guidance. More important rules should be enforced structurally.

Dependency checks can prevent forbidden imports. CODEOWNERS can require the right reviewers. Schemas can validate configuration. Linters can reject unsafe patterns. Policy as code can block infrastructure changes that violate organizational rules.

Documentation helps the AI choose the right path. Automated enforcement prevents the wrong path from silently merging.

Treat Generated Code as Untrusted

Untrusted does not mean bad. It means the output must earn trust through review and verification.

Read the diff. Run the tests. Check dependency changes. Inspect error handling and empty states. Verify commands and configuration examples. Look for deleted behavior. Confirm that secrets, logs, and user data are handled correctly.

Also ask what the tool assumed.

Did it invent an API? Did it choose a version that does not exist in the environment? Did it interpret an ambiguous requirement as permission to redesign a shared component? Did it handle only the happy path from the prompt?

AI produces useful drafts. Engineering turns those drafts into dependable systems.

Build a Safe Default Workflow

A practical AI coding workflow should make the safe path the easy path:

  1. Work in an isolated branch or checkout.
  2. Limit write access to the project in scope.
  3. Define the expected behavior with tests or acceptance checks.
  4. Ask for a small, focused change.
  5. Run formatting, static analysis, tests, and security checks automatically.
  6. Review the diff and any dependency changes.
  7. Require explicit approval for publishing, deployment, or destructive actions.
  8. Keep a recovery path for the change.

This workflow does not depend on the AI always making the correct decision. It assumes mistakes are possible and makes them cheap to catch.

That is how reliable engineering works with humans too.

Final Thought

Better prompts improve AI output, but prompts alone cannot carry the weight of engineering control.

Use prompts to explain the goal and context. Use permissions to limit reach. Use tests to protect behavior. Use repository rules and policy checks to enforce architecture. Use human approval where actions have meaningful external impact.

Constraints do not make an AI coding tool less capable. They make its capability predictable enough to use.

The goal is not to trust every generated line. The goal is to build a process where trust is earned before the line reaches production.