AI-Assisted Development: Copilot, Prompt Engineering, and AI Workflows
Use AI tools productively as an engineer ā GitHub Copilot patterns, effective prompting for code generation, AI-assisted debugging and refactoring, and the workflows that make AI a genuine force multiplier.
AI Is a Skill, Not a Magic Button
Engineers who use AI tools well are significantly faster than those who don't. But AI tools used poorly produce confident-sounding code that doesn't work. The skill is knowing how to communicate precisely, verify output critically, and integrate AI into a workflow that improves quality ā not just speed.
1. The Landscape of AI Dev Tools
| Tool | Best For | |------|---------| | GitHub Copilot | In-editor autocomplete, inline generation | | ChatGPT / Claude | Complex reasoning, architecture, debugging | | Cursor | AI-native editor (Copilot++ with full project context) | | Codeium | Free Copilot alternative | | Amazon Q (CodeWhisperer) | AWS-specific suggestions | | Claude Code | Terminal-based agentic coding |
2. GitHub Copilot
Setup
# VS Code: install GitHub Copilot extension
# JetBrains: install GitHub Copilot plugin
# Neovim: copilot.vimKeyboard shortcuts (VS Code)
| Action | Shortcut |
|--------|---------|
| Accept suggestion | Tab |
| Dismiss | Esc |
| Next suggestion | Alt+] |
| Previous suggestion | Alt+[ |
| Open Copilot panel (multiple suggestions) | Ctrl+Enter |
| Copilot Chat | Ctrl+Shift+I |
Making Copilot smarter ā context matters
Copilot reads the current file and open tabs. Give it signal:
# WEAK ā vague function signature
def process():
pass
# STRONG ā descriptive name + type hints + docstring comment
def normalize_customer_email(raw_email: str) -> str:
# Remove whitespace, lowercase, validate format. Return empty string if invalid.# Write a descriptive comment, then let Copilot complete the implementation
def calculate_cohort_retention(
orders: pd.DataFrame,
cohort_date_col: str = "first_order_date",
activity_date_col: str = "order_date",
) -> pd.DataFrame:
# Group customers by cohort month (month of first order).
# For each cohort, calculate the % of customers who ordered
# in each subsequent month (month 0, 1, 2, ...).
# Return a pivot table: cohort_month Ć months_since_first_order ā retention_rate.Copilot for tests (very effective)
def calculate_tax(amount: float, rate: float) -> float:
return round(amount * rate, 2)
# Type: def test_ ... and Copilot generates:
def test_calculate_tax_standard():
assert calculate_tax(100.0, 0.25) == 25.0
def test_calculate_tax_zero_amount():
assert calculate_tax(0.0, 0.25) == 0.0
def test_calculate_tax_rounding():
assert calculate_tax(1.005, 1.0) == 1.0Copilot for SQL (very effective)
-- Get the top 5 customers by total revenue in the last 30 days,
-- showing customer_id, name, order_count, total_revenue, and avg_order_valueCopilot generates:
SELECT
c.customer_id,
c.full_name,
COUNT(DISTINCT o.order_id) AS order_count,
SUM(o.amount_usd) AS total_revenue,
AVG(o.amount_usd) AS avg_order_value
FROM dim_customers c
JOIN fct_orders o ON c.customer_sk = o.customer_sk
WHERE o.order_date >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY 1, 2
ORDER BY total_revenue DESC
LIMIT 5;3. Prompt Engineering for Code
Prompt engineering for code is about giving the model enough context to produce exactly what you need on the first try.
The anatomy of a good coding prompt
1. Role/context: Who you are and what system you're working with
2. What you want: The specific task
3. Constraints: Type system, libraries, patterns, style
4. Input/output: What data goes in, what comes out
5. Examples (optional): Show don't tellBad vs good prompts
Bad:
Write a Python function to process data
Good:
Write a Python function
clean_orders(df: pd.DataFrame) -> pd.DataFramethat:
- Removes rows where
order_idoramount_usdis null- Strips and lowercases the
statuscolumn- Converts
created_atto UTC-aware datetime usingpd.to_datetimewithutc=True- Returns the cleaned DataFrame with the same column names Use pandas 2.x, type hints, and no comments.
4. Prompting Patterns by Task
Generating a new function
Context: Python 3.12, pandas 2.x, Snowflake.
Write a function `fetch_incremental_orders(last_run_dt: datetime) -> pd.DataFrame`
that queries Snowflake using snowflake-connector-python, returns orders where
created_at > last_run_dt. Use a context manager for the connection.
Environment variables: SNOWFLAKE_ACCOUNT, SNOWFLAKE_USER, SNOWFLAKE_PASSWORD.Debugging
This Python code is producing wrong results. The function is supposed to
calculate 7-day rolling average but I'm getting NaN for the first 7 rows
when I expect values from day 1.
Code:
df['rolling_avg'] = df['revenue'].rolling(7).mean()
What's wrong, and what's the correct way to get a rolling average that
includes partial windows?Code review
Review this dbt incremental model for bugs or anti-patterns. Focus on:
1. Is the incremental filter correct?
2. Will the unique_key handle deduplication properly?
3. Any Snowflake-specific issues?
[paste your SQL]Explaining code
Explain this SQL window function to someone who knows basic SQL but
has never used window functions. Walk through it step by step,
explaining what PARTITION BY, ORDER BY, and ROWS BETWEEN do:
SUM(amount_usd) OVER (
PARTITION BY customer_id
ORDER BY order_date
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS cumulative_spendGenerating tests
Write pytest unit tests for this function. Include:
- Happy path
- Edge case: empty list
- Edge case: single element
- Edge case: all identical values
- Error case: non-numeric input
Use pytest, no mocking needed.
def moving_average(values: list[float], window: int) -> list[float]:
...Refactoring
Refactor this function to:
1. Replace the nested if-else with guard clauses
2. Extract the validation logic into a separate function
3. Add type hints throughout
4. Keep the same behavior
[paste code]5. AI-Assisted Debugging Workflow
Step 1: Isolate the problem
"I'm getting this error: [paste full traceback].
Here's the relevant code: [paste the function/file].
Here's what I expected: [describe expected behavior].
Here's what actually happens: [describe actual behavior]."
Step 2: Generate hypotheses
"What are the 3 most likely causes of this error?"
Step 3: Verify with targeted questions
"Could this be caused by [specific hypothesis]? Here's the relevant config: ..."
Step 4: Get a fix
"Given that the problem is [root cause], write the corrected version of [function]."
Step 5: Verify the fix
"Does this fix introduce any new bugs or edge cases I should test?"Template for complex bugs
I'm debugging a data pipeline issue in Python/Snowflake.
**Symptom:** Row counts in silver.orders drop by ~30% on Mondays
**Expected:** Similar row counts every day (~1200 rows)
**Actual:** ~850 rows on Mondays
**Relevant code:**
[paste incremental model / Airflow DAG]
**Data checked:**
- Bronze table has correct row count on Mondays ā
- The issue appears after the silver model runs ā
What should I check next?6. AI for Documentation
Write a docstring for this function. Use Google style.
Be concise ā just Args, Returns, Raises.
Don't repeat information that's obvious from the type hints.
def merge_customer_dimensions(
staging: pd.DataFrame,
existing: pd.DataFrame,
compare_columns: list[str],
) -> tuple[pd.DataFrame, int]:
...Write a dbt model description for schema.yml.
The model: one row per customer, derived from the CRM and ERP sources,
with a business-rule-derived segment column. Used by the revenue dashboard.
Keep it under 3 sentences.7. What AI Is Bad At (Critical Awareness)
AI will confidently produce wrong code for:
- Complex SQL with subtle semantics (SCD Type 2 MERGE, window frame boundaries)
- Edge cases it hasn't seen (empty DataFrames, timezone-naive datetimes)
- Library-version-specific APIs (e.g., Pandas 2.x vs 1.x behavior)
- Business logic it doesn't know about
- Security-sensitive code (input sanitization, auth flows)
Your job as an engineer:
ā Read every line of AI-generated code before using it
ā Run the tests ā don't trust "this should work"
ā Know the domain well enough to spot wrong answers
ā Check for hallucinated library methods (e.g., `.group_by()` on a pandas DataFrame)
ā Be especially skeptical of security-related code8. Claude Code (Terminal AI Agent)
Claude Code is an agentic AI that runs in your terminal with full access to your filesystem:
# Install
npm install -g @anthropic-ai/claude-code
# Use
claude
# Useful commands
/help # list all commands
/clear # clear context
/memory # view memory
/compact # compress conversation context# Example terminal prompts:
"Read src/pipeline.py and identify any SQL injection risks"
"Add type hints to all functions in src/ingestion/"
"Write tests for the validate_orders function in src/validators.py"
"Find all places where we read from snowflake.orders and check if any use SELECT *"9. Effective AI Workflow Integration
The 30/70 Rule
AI writes the first draft (30% of the time)
You review, fix, extend, and make it production-quality (70% of the time)When to use AI vs when to think yourself
| Use AI | Think Yourself | |--------|---------------| | Boilerplate (argparse setup, Dockerfile, etc.) | Core business logic | | Standard patterns (retry decorator, pagination) | Security-sensitive code | | First draft of tests | System architecture decisions | | Explaining unfamiliar code | Trade-off analysis | | Generating SQL for known schema | Data model design | | Writing documentation | Code review judgments |
Daily AI-assisted workflow
1. Start with a plan (write it yourself or ask AI to draft, then edit)
2. Use Copilot for autocomplete during implementation
3. When stuck: ask Claude/ChatGPT with full context
4. Use AI to generate test cases
5. Use AI for first draft of PR description
6. Use AI to explain code in PRs you're reviewingSummary
| Tool | Best Use | |------|---------| | GitHub Copilot | Inline autocomplete, test generation, boilerplate | | ChatGPT / Claude | Debugging, architecture, complex code generation | | Cursor | Full project context, AI-powered refactoring | | Prompt pattern | Role + task + constraints + input/output | | Debugging template | Symptom + expected + actual + relevant code | | Critical review | Always read AI code ā never trust blindly | | Claude Code | Terminal agent for file-level tasks |
The engineers who thrive are those who use AI as a force multiplier while retaining the engineering judgment to know when AI output is wrong. Learn the fundamentals deeply so you can spot the mistakes.
Found this helpful?
Leave a comment
Have a question, correction, or just found this helpful? Leave a note below.