Back to blog
AI Systemsbeginner

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.

LearnixoMay 7, 20269 min read
AIGitHub Copilotprompt engineeringChatGPTClaudeproductivitydevelopment tools
Share:š•

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

Bash
# VS Code: install GitHub Copilot extension
# JetBrains: install GitHub Copilot plugin
# Neovim: copilot.vim

Keyboard 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:

Python
# 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.
Python
# 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)

Python
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.0

Copilot for SQL (very effective)

SQL
-- Get the top 5 customers by total revenue in the last 30 days,
-- showing customer_id, name, order_count, total_revenue, and avg_order_value

Copilot generates:

SQL
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 tell

Bad vs good prompts

Bad:

Write a Python function to process data

Good:

Write a Python function clean_orders(df: pd.DataFrame) -> pd.DataFrame that:

  • Removes rows where order_id or amount_usd is null
  • Strips and lowercases the status column
  • Converts created_at to UTC-aware datetime using pd.to_datetime with utc=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_spend

Generating 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 code

8. Claude Code (Terminal AI Agent)

Claude Code is an agentic AI that runs in your terminal with full access to your filesystem:

Bash
# 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 reviewing

Summary

| 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.

Enjoyed this article?

Explore the AI Systems learning path for more.

Found this helpful?

Share:š•

Leave a comment

Have a question, correction, or just found this helpful? Leave a note below.