7 AI Coding Tips: Boost Productivity with Google Antigravity Prompts
Introduction There's a widening gap between developers who use AI and developers who leverage it. Both groups have access to the same models. Both pay for the s...
Introduction
There's a widening gap between developers who use AI and developers who leverage it. Both groups have access to the same models. Both pay for the same subscriptions. Yet one group ships features in an afternoon while the other spends that same afternoon re-explaining their codebase to a chatbot that keeps forgetting the architecture.
The difference isn't the model. It's the prompt.
As AI coding tools have matured, raw model capability has become commoditized. What separates productive AI-assisted workflows from frustrating ones is prompt quality—how well you front-load context, constrain scope, and structure multi-step work. Prompt engineering is no longer a novelty skill; it's the new productivity bottleneck.
This article is written for intermediate to advanced developers who are already comfortable with AI assistants and want to move from "AI helps me sometimes" to "AI reliably ships production code." We'll cover 7 actionable techniques, each with real prompt templates, code snippets, and measurable workflow gains.
A quick note on Google Antigravity: unlike chat-only tools, Antigravity is built around an agent-first, multi-file editing model. The agent can read your repository, edit multiple files, and run commands. That power cuts both ways—a vague prompt in a chat window produces a bad answer; a vague prompt in Antigravity produces a bad commit. Prompt structure matters here more than anywhere else, because the agent acts on your instructions rather than just describing what it would do.
Let's dive in.
Tip 1: Front-Load Context with a "Project Brief" Prompt
Why It Works
Every clarification round-trip is wasted time. When you start a session without context, the agent guesses at your stack, your conventions, and your constraints—and it guesses wrong. A reusable project brief eliminates that overhead and anchors the agent to your actual architecture from the first token.
The Technique
Build a context block once, store it in your repo, and paste or reference it at the start of major sessions. Include: stack and versions, architectural patterns, testing framework, style rules, and hard constraints (e.g., "no new dependencies without approval").
Prompt Example
You are working in a TypeScript monorepo. Context:
- Stack: Node 20, TypeScript 5.4, Fastify, PostgreSQL via Drizzle ORM
- Architecture: hexagonal; domain logic in /core, adapters in /infra
- Testing: Vitest, tests colocated as *.test.ts
- Style: no default exports, explicit return types, Result<T,E> for errors
- Constraints: no new runtime dependencies; all DB access via repositories
Task: [your task here]
Code Snippet
A CONTEXT.md file at the repo root—or .antigravity/context if you prefer a dedicated directory—keeps this versioned alongside your code:
# .antigravity/context
## Stack
Node 20 · TypeScript 5.4 · Fastify · Drizzle · PostgreSQL
## Conventions
- Hexagonal architecture: domain in /core, I/O in /infra
- Errors: Result<T, E>, never throw across module boundaries
- Tests: Vitest, colocated, one describe per exported symbol
- No default exports. Explicit return types on all public functions.
## Commands
- `pnpm test` — run unit tests
- `pnpm lint` — ESLint + Prettier check
Before/After Comparison
Before: 5–8 clarification round-trips per task ("What ORM are you using?" "Should I add a dependency?" "Where do tests live?").
After: First-response accuracy on architectural tasks improves dramatically—the agent proposes changes that match your patterns instead of generic boilerplate.
Tip 2: Constrain the Scope with Explicit File and Function Boundaries
Why It Works
Agentic tools are eager. Ask for a small fix and you may get a refactor of three unrelated files. Every unrequested change is review burden, merge conflict risk, and cognitive overhead. Explicit boundaries keep the diff tight.
The Technique
Name the exact files, functions, and—when helpful—line ranges. Explicitly state what the agent must not touch.
Prompt Example
Modify only `auth/session.ts`, function `refreshToken()`.
Do not touch callers, do not rename exports, do not reformat
unrelated code. Keep the public signature identical.
Code Snippet
Unscoped prompt result (bad):
- export async function refreshToken(t: string) {
+ export async function refreshToken(token: string, opts?: RefreshOpts) {
+ // also refactored login() and logout() below...
Scoped prompt result (good):
export async function refreshToken(t: string) {
- const payload = jwt.decode(t);
+ const payload = jwt.verify(t, SECRET) as JwtPayload;
return sign({ sub: payload.sub }, SECRET, { expiresIn: '1h' });
}
Before/After Comparison
Before: Large diffs requiring manual pruning—you spend 20 minutes reverting changes you never asked for.
After: Reviewable, merge-ready patches on the first pass. Review time drops from minutes to seconds.
Tip 3: Use Test-First Prompts to Lock in Behavior
Why It Works
Vague requirements produce vague code. Tests turn requirements into verifiable acceptance criteria—and they let the agent self-check before you ever open the diff.
The Technique
Instruct the agent to write failing tests first, then implement until green. This forces it to reason about edge cases explicitly.
Prompt Example
Write failing Vitest tests for `parseDuration()` covering:
- "1h30m" → 5400
- "0s" → 0
- invalid input → Result.err
- negative values → Result.err
Then implement until all tests pass. Do not modify the tests.
Code Snippet
// parseDuration.test.ts
describe('parseDuration', () => {
it('parses compound units', () => {
expect(parseDuration('1h30m')).toEqual({ ok: true, value: 5400 });
});
it('rejects negatives', () => {
expect(parseDuration('-5m').ok).toBe(false);
});
});
// parseDuration.ts (generated after tests)
export function parseDuration(input: string): Result<number, string> {
const match = input.match(/^(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$/);
if (!match || input === '') return { ok: false, error: 'invalid' };
const [, h, m, s] = match;
return { ok: true, value: (+h || 0) * 3600 + (+m || 0) * 60 + (+s || 0) };
}
Before/After Comparison
Before: Manual QA cycles catching regressions late, often in staging.
After: Regressions caught at generation time. The agent iterates against its own tests before you see the code.
Tip 4: Chain Prompts for Multi-Step Refactors
Why It Works
Large refactors fail when requested in one shot—the agent loses track of dependencies and produces partially broken code. Chaining mirrors how senior engineers decompose work: one verifiable step at a time.
The Technique
Break the refactor into sequential, dependent prompts with checkpoints. Verify each step compiles and passes tests before proceeding.
Prompt Example
Step 1: Extract an interface `UserStore` from the concrete `PgUserStore`.
Do not change callers yet.
Step 2: Migrate all callers to depend on `UserStore` instead of `PgUserStore`.
Step 3: Delete `PgUserStore`'s now-unused public methods.
Code Snippet
After Step 1:
export interface UserStore {
findById(id: string): Promise<User | null>;
}
export class PgUserStore implements UserStore { /* ... */ }
After Step 2:
export class AuthService {
constructor(private users: UserStore) {}
}
Before/After Comparison
Before: One mega-prompt producing partially broken code and a 400-line diff you can't safely review.
After: Incremental, always-compiling states. Each step is independently testable and revertible.
Tip 5: Ask for Trade-off Analysis, Not Just Code
Why It Works
The most expensive bugs are design bugs. Asking for alternatives before committing surfaces decisions you'd otherwise discover in code review—after the work is done.
The Technique
Prompt for 2–3 approaches with pros and cons across the dimensions that matter: latency, complexity, maintenance, risk.
Prompt Example
Give three approaches to caching this query. Compare latency,
implementation complexity, and invalidation risk. Recommend one
for a read-heavy workload with infrequent writes.
Code Snippet
Option A — in-process LRU:
const cache = new LRU<string, User>({ max: 1000, ttl: 60_000 });
Option B — Redis with explicit invalidation:
const cached = await redis.get(`user:${id}`);
if (!cached) {
const user = await db.users.findById(id);
await redis.set(`user:${id}`, JSON.stringify(user), 'EX', 300);
}
The trade-off table the agent produces lets you pick Option A for simplicity or Option B for cross-instance consistency—deliberately.
Before/After Comparison
Before: Rewriting after a design dead-end discovered in review.
After: Choosing the right approach up front, with documented reasoning.
Tip 6: Encode Your Team's Conventions as Reusable Prompt Fragments
Why It Works
Consistency across contributors and sessions is hard enough with humans. Prompt fragments make your conventions machine-enforceable, driving style-related review comments to near zero.
The Technique
Maintain a library of composable snippets covering error handling, logging, naming, and commit format. Prepend the relevant fragments to any prompt.
Prompt Example
Fragments:
- Use our Result<T, E> pattern; never throw across module boundaries.
- Log via `logger.info({ event, ...ctx })`, never `console.log`.
- Commit messages: `type(scope): imperative summary`.
Code Snippet
Before (throws):
export function loadConfig(path: string): Config {
const raw = fs.readFileSync(path, 'utf8');
return JSON.parse(raw); // throws across boundary
}
After (convention-compliant):
export function loadConfig(path: string): Result<Config, ConfigError> {
try {
return { ok: true, value: JSON.parse(fs.readFileSync(path, 'utf8')) };
} catch (e) {
return { ok: false, error: { kind: 'parse', cause: e } };
}
}
Before/After Comparison
Before: Repeated style corrections in every PR—"please use Result here," "no console.log."
After: Convention-compliant output by default. Reviewers focus on logic, not lint.
Tip 7: Close the Loop with Self-Review and Diff Summaries
Why It Works
The agent shouldn't just generate code—it should review it. A self-review pass catches logic errors, missing edge cases, and unintended side effects before a human ever looks.
The Technique
After generating changes, prompt the agent to critique its own diff, list risks, and summarize what changed and why.
Prompt Example
Review the diff you just produced. List:
1. Any logic errors or unhandled edge cases
2. Files changed outside the requested scope
3. A one-paragraph summary suitable for a PR description
If you find issues, fix them and re-summarize.
Code Snippet
Agent's self-review output:
## Self-Review
- ⚠️ `refreshToken` doesn't handle expired tokens → added try/catch
- ✅ No files changed outside `auth/session.ts`
- Summary: Hardened token refresh to verify signatures and reject
expired tokens; added two tests covering both paths.
Before/After Comparison
Before: You are the first reviewer, catching obvious issues the agent could have caught itself.
After: You review a pre-vetted diff with a written rationale. Your attention goes to architecture, not typos and missed null checks.
Conclusion
The seven tips above share a common thread: they treat prompting as engineering, not conversation. Front-load context. Constrain scope. Lock behavior with tests. Chain complex work. Ask for trade-offs. Encode conventions. Close the loop with self-review.
In Google Antigravity's agent-first model, these habits aren't optional refinements—they're the difference between an agent that ships merge-ready patches and one that generates cleanup work. Start with the project brief (Tip 1) and scope constraints (Tip 2); they deliver the largest immediate gains. Layer in the rest as your workflow matures.
The developers pulling ahead aren't using better models. They're writing better prompts. Now you can too.