AI Code Assistant Adoption
Best practices for integrating AI coding tools into day-to-day engineering workflows.
Overview
AI coding assistants have moved from novelty to standard tooling. They accelerate boilerplate, surface patterns, generate first drafts of tests and documentation, and reduce the time spent on routine mechanical work. Used well, they free engineers to spend more attention on the things that require human judgment: architecture, correctness, intent, and trade-offs.
Used poorly, they introduce a subtler problem than the one they solve: code that looks right, compiles, and passes tests — but contains hidden logic errors, security gaps, or missed edge cases that surface later and are harder to trace. The assistant did not think; it predicted. The engineer who committed the output is still responsible for it.
This page covers how we integrate AI coding tools into our workflow, which tools we use, and the standards that keep AI assistance an accelerator rather than a liability.
Why It Matters
Routine tasks have a real time cost. Writing boilerplate, generating type definitions from an API response, scaffolding test files, writing docstrings — these are necessary but low-judgment tasks. An AI assistant completes them in seconds. That time compounds across a team.
Cognitive bandwidth is finite. An engineer who spends three hours writing unit tests for a data transformation layer has three fewer hours for architecture review and design decisions. AI assistance on the mechanical layer preserves bandwidth for the judgment layer.
The quality of code review improves when AI handles the obvious. When an AI assistant catches formatting violations, missing null checks, and obvious type errors before a PR opens, human reviewers can spend their attention on logic, architecture, and intent — the things AI cannot reliably evaluate.
Adoption without standards creates inconsistency. Teams that let every engineer decide independently how and when to use AI assistance get inconsistent code quality, inconsistent security practices, and inconsistent expectations in code review. Standards make the benefit predictable and the risk manageable.
Standards & Best Practices
AI output is a first draft, not a finished commit
Treat AI-generated code the way you treat a junior engineer's code: read it, understand it, test it, and take responsibility for it before committing. "The AI wrote it" is not a defence when a bug reaches production. The engineer who committed the code owns the code.
Apply the same standards to AI-generated code that you apply to hand-written code — same linting, same type-checking, same tests, same code review.
Choose by integration model, not by brand
AI coding tools fall into three integration models. The models are stable; the specific tools filling each model change as the ecosystem evolves. Choose by model first — then pick the current best-fit tool for that model.
| Integration model | How it works | Current examples | Best for |
|---|---|---|---|
| CLI / agentic | Runs in the terminal alongside your editor; reads and writes files directly; can execute commands | Claude Code | Multi-file refactoring, agentic tasks, terminal-first workflows |
| IDE-native | Embedded in or replaces the editor; delivers inline completions as you type | Cursor, GitHub Copilot | Daily editing, rapid file-level changes, autocomplete-heavy workflows |
| Chat / conversational | Web or desktop interface with no automatic codebase access; context comes from explicit paste | ChatGPT, Claude.ai, Gemini | Design exploration, debugging questions, one-off tasks |
These models are not competing — they address different moments in a workflow. A team commonly uses an IDE-native tool for daily editing, a CLI/agentic tool for complex multi-file refactors, and a conversational tool for design exploration.
When a new tool launches, ask: which model does this fill? That immediately tells you when to reach for it and what to expect from it.
Never send secrets to an AI tool
API keys, database passwords, session tokens, private keys, and any other credentials must never appear in a prompt or in code you paste into an AI chat. This applies to all three tools — all send your input to external servers.
Before pasting a file or a code block, scan it for secrets. Strip them and replace with placeholders (<REDACTED>) if the surrounding code structure is what you need the AI to reason about.
Understand codebase awareness by integration model
| Integration model | Codebase access | How to provide project context |
|---|---|---|
| CLI / agentic | Reads files on demand; project-level instructions via a config file (e.g. CLAUDE.md, AGENTS.md) | Create a project instructions file at the repo root; the tool reads it automatically |
| IDE-native | Indexes the full codebase semantically; queries it at edit time | Configure indexing once; works automatically thereafter |
| Chat / conversational | None by default — sees only what you paste | Include relevant types, function signatures, and dependencies explicitly in every prompt |
Context-aware models (CLI and IDE-native) produce higher-quality output because they understand the project structure without you having to explain it. For conversational tools, compensate with explicit context in every prompt — paste the relevant code and constraints alongside your question.
Prompt for specificity, not volume
A clear, constrained prompt produces better output than a long one. Define what "done" looks like before you prompt.
| Weak prompt | Strong prompt |
|---|---|
| "Write tests for this" | "Write Jest unit tests for this calculateDiscount function. Cover: zero input, negative price, missing coupon code, and the happy path. Use describe/it blocks. No mocks." |
| "Refactor this code" | "Extract the validation logic from this function into a separate validateCheckoutPayload function. Keep the types. Don't change the tests." |
| "Fix the bug" | "This function returns undefined when user.address is null. Add a null guard and return an appropriate error value — don't throw." |
Specify the output format, the scope, the constraints, and what not to change. Ambiguity produces output you'll have to rewrite.
Review AI output at the logic level, not just the surface
Syntax and type errors are easy to spot. The harder problems in AI-generated code are:
- Tautological tests — tests that assert what the implementation does rather than what it should do
- Missing edge cases — AI tends to handle the happy path and obvious error cases, not the subtle ones
- Incorrect error handling — errors silently swallowed, wrong error types thrown, or recovery logic missing
- Race conditions — async hazards are a documented weak spot for AI-generated code
- Over-generalisation — abstractions added that aren't needed, making the code harder to read
Read the logic, not just the shape. A function that compiles and passes its generated tests may still be wrong.
Establish a team norm for AI disclosure in PRs
Teams that are transparent about AI-assisted code review it more carefully — which is the right instinct. A simple convention in the PR description (e.g., "AI-assisted: test scaffolding generated with Cursor, reviewed and modified") keeps reviewers calibrated without creating overhead.
How to Implement
Step 1 — Choose your primary tool and configure it
Pick one primary tool per engineer based on their workflow. Configuration matters more than tool choice.
Claude Code:
# Install via npm
npm install -g @anthropic-ai/claude-code
# Or via Homebrew (macOS)
brew install claude-codeAdd a CLAUDE.md at the project root with team standards. Claude reads this at the start of every session:
# Project Standards
## Language & framework
- TypeScript, Next.js 15, React 19
- Use pnpm, not npm or yarn
## Code style
- No comments unless the WHY is non-obvious
- Prefer `const` + arrow functions for utilities
- All new components must have a corresponding test file
## What to avoid
- Do not use `any` without a type comment explaining why
- Do not add error handling for scenarios that can't happenCursor:
Add .cursor/rules files to set context for specific directories. Cursor reads these when editing files in that directory:
# .cursor/rules (project root)
This project uses TypeScript strict mode. Always provide explicit return types.
Prefer server components for data fetching. Use 'use client' only when browser APIs are needed.OpenAI Codex: No project-level config. Establish a team prompt library — a shared document of reusable prompt patterns for common tasks (test generation, refactoring, documentation).
Step 2 — Define which tasks AI assistance is appropriate for
Write a brief team agreement on where AI assistance is expected vs. where it should be flagged:
| Task category | AI assistance | Notes |
|---|---|---|
| Boilerplate, scaffolding | Encouraged | Always review before committing |
| Unit test generation | Encouraged | Verify tests are meaningful, not tautological |
| Documentation / docstrings | Encouraged | Review for accuracy |
| Complex business logic | With caution | AI output needs close review; consider writing by hand |
| Security-sensitive code | Requires review | Auth, cryptography, permissions — human sign-off required |
| Architecture decisions | Not appropriate | AI can explain trade-offs; decisions must be human-made |
Step 3 — Adopt a standard review checklist for AI-generated code
Before committing any AI-generated or AI-assisted code:
- I have read and understood every line
- The logic is correct, not just syntactically valid
- Edge cases and error conditions are handled
- No secrets, credentials, or environment-specific values are present
- The code passes the team's standard linting and type-checks
- If tests were AI-generated: I have verified they would fail if the implementation were broken
Step 4 — Set up linting and type-checking as a backstop
AI tools respect your linting config if they can see it. With Claude Code and Cursor, the config is read automatically. With Codex, include relevant rules in the prompt.
CI lint and typecheck jobs catch issues that slip through. They are not a substitute for review, but they are a reliable baseline:
# Runs on every PR — catches AI-generated code that doesn't meet standards
- name: Lint
run: pnpm lint
- name: Type-check
run: pnpm typecheckStep 5 — Run a team retrospective after the first 30 days
After adopting AI tooling, hold a 30-minute retrospective:
- Where did AI assistance save real time?
- Where did it produce output that needed significant rework?
- Did any AI-generated code reach review in a state that wasted reviewer time?
- Are there tasks the team wants to standardise prompts for?
Use the findings to refine the team agreement from Step 2.
Tools & Templates
Integration model capability comparison
| Capability | CLI / agentic | IDE-native | Chat / conversational |
|---|---|---|---|
| Codebase access | Full repo (reads files on demand) | Full repo (semantic index) | Manual paste only |
| File editing | Direct edits with git integration | Inline edits within editor | Copy/paste only |
| Terminal access | Full CLI, command execution | None | None |
| Completions | On request | Real-time inline | On request |
| Agentic / multi-step tasks | Yes — multi-file, multi-step | Limited background agents | None |
| Typical entry cost | Subscription | Free tier available | Free tier available |
| Current examples | Claude Code | Cursor, GitHub Copilot | ChatGPT, Claude.ai, Gemini |
Reusable prompt patterns
Generate unit tests:
Write Jest unit tests for the `[function name]` function below.
Cover: [list specific cases].
Use describe/it blocks. Do not mock unless I/O is involved.
A test should fail if the implementation is wrong — verify this.
[paste function]Extract a function:
Extract the [describe logic] into a separate function called `[name]`.
Keep the existing types. Do not change the calling code or the tests.
Return only the new function and the updated call site.Explain unfamiliar code:
Explain what this code does, step by step.
Identify any edge cases or assumptions that are not obviously handled.
Note anything that looks like it might be a bug.
[paste code]Common Pitfalls
Committing AI output without reading it. Speed is the temptation; correctness is the requirement. Code that looks plausible and compiles may still be logically wrong. Read every line before committing.
Sending secrets in prompts. Environment variables, API keys, and connection strings pasted into a chat window have left your control. Strip all secrets before sharing any code with an AI tool.
Trusting AI-generated tests as proof of correctness. A test generated by the same system that wrote the implementation is likely to encode the same assumptions. Verify that generated tests would actually fail if the implementation had a bug in it.
Using AI for architecture decisions. AI assistants can describe trade-offs fluently. They cannot weigh your team's specific constraints, organisational context, or long-term maintenance burden. Use AI to explore options; make decisions as a team.
Picking a tool based on hype, not workflow fit. AI tools serve different integration models — CLI/agentic, IDE-native, and chat/conversational. Evaluate based on which model fits how your team actually works, not benchmarks measured in controlled conditions.
No team norms leading to inconsistent practices. If some engineers review AI output carefully and others commit it directly, the team has no shared standard and code review becomes unpredictable. Agree on norms explicitly; don't assume shared intuition.
AI Governance & Acceptable Use
What data can go into AI tools, what cannot, and the standards that protect the organisation while letting AI help.
Context Engineering
How to build and maintain the context files that make AI tools effective — and how to treat context as a first-class engineering artefact.