Optional appendix: advanced AI workflows and prompting
This material is optional. It is not tied to a lab, and nothing here is required for the final project. Work through it when you want more deliberate habits for the tools you already use.
Part 1: advanced prompting
Part 2: repeatable AI workflows
A useful workflow can:
The important question is not whether AI was used. It is whether the final result is correct, understood, tested, and appropriate for the project.
Poor prompt:
write a function to sort a list
Better prompt:
Write a Python function that sorts a list of dictionaries by a
specified key. Include type hints, docstring, and handle edge
cases like missing keys.
More complete prompt:
I'm building a data processing pipeline in Python. I need a
function that:
- Takes a list of dictionaries and a key name
- Sorts the list by that key's value
- Handles missing keys gracefully (should those items go first or last?)
- Returns a new sorted list (don't modify original)
- Include NumPy-style docstring and type hints
- Should this be case-sensitive for string values?
Example input: [{"name": "Alice", "age": 30}, {"name": "Bob"}]
1. Context - Provide background information
I'm working on a Rust CLI tool for processing log files...
2. Constraints - Specify requirements and limitations
Must use only standard library, no external dependencies
3. Clarity - Be specific and unambiguous
Parse Apache Common Log Format, not generic logs
4. Success criteria - State how the result will be evaluated
Input: representative `access.log` fixture
Output: JSON summary of status codes
Check: fixture output matches the expected counts
5. Evidence - Request checks or sources that can be inspected
That's good, but can you make it handle malformed lines
without crashing?
Technique: Name the review perspective and the evidence you want
Generic approach:
How do I optimize this Python code?
Specific review approach:
Review this function for performance risks in a high-traffic
web service. Identify the suspected bottleneck, explain what
measurement would confirm it, and propose the smallest change
worth benchmarking.
Examples:
Technique: Ask for a concise explanation that you can check
Standard prompt:
Why doesn't this Rust code compile?
[code snippet]
Evidence-focused prompt:
This Rust code doesn't compile. Using the compiler message:
1. Identify the conflicting borrows and their source lines.
2. State the ownership rule involved.
3. Propose two fixes and explain their trade-offs.
4. Show the smallest corrected example.
5. Give the Cargo command that verifies the fix.
[code snippet]
Benefits: - Grounds the explanation in visible code and compiler output - Produces claims that can be checked independently - Separates alternative fixes from the chosen fix - Ends with an executable verification step
Technique: Provide examples of desired input/output
Without examples (zero-shot):
Convert this function to use type hints
With examples (few-shot):
Convert this function to use type hints. Follow this style:
Example 1:
Input: def add(a, b): return a + b
Output: def add(a: int, b: int) -> int: return a + b
Example 2:
Input: def greet(name): return f"Hello, {name}"
Output: def greet(name: str) -> str: return f"Hello, {name}"
Now convert this function:
def process_data(items, threshold):
return [x for x in items if x > threshold]
When to use: - Specific output formatting - Consistent code style - Complex transformations - Domain-specific patterns
Problem: AI tools have limited context windows
Strategy 1: Chunking
First, let's focus on the data structures (send relevant code)
[AI responds]
Now let's look at the processing logic (send next chunk)
Strategy 2: Explicit references
In the validate_input() function we just discussed, add
error handling for the case where...
Strategy 3: Summary anchors
To summarize what we've built so far:
- User input validation with regex patterns
- SQLite database connection with connection pooling
- Error handling using Result types
Now let's add the query builder...
Strategy 4: Start fresh when needed
Let's start a new conversation. Here's the complete current
state of the module... [provide full context]
Effective review prompt:
Review this Rust function for:
1. **Correctness**: Logic errors, edge cases, type safety
2. **Performance**: Unnecessary allocations, algorithmic complexity
3. **Idiomatic Rust**: Better patterns, standard library usage
4. **Security**: Input validation, potential vulnerabilities
5. **Maintainability**: Naming, documentation, structure
For each issue found:
- Explain WHY it's a problem
- Show HOW to fix it
- Rate severity (critical/moderate/minor)
[paste code]
Better than:
Is this code good?
Reviewing for performance:
Prompt:
Review this Python function for performance issues. What's
the time complexity? How could we improve it? Show a more
efficient implementation using appropriate data structures.
Evaluate the response: - Does it identify the actual complexity from the supplied code? - Does the proposed data structure preserve ordering and duplicate behavior? - Do tests distinguish the original and revised implementations? - Does a benchmark support any performance claim?
Focused debugging prompt:
I'm getting this error in my Rust program:
[paste error message]
Here's the relevant code:
[paste minimal reproducing code]
Context:
- Using the repository's current stable Rust toolchain
- This worked before I added the new caching feature
- Error only happens with concurrent requests
Please:
1. Explain what's causing this error
2. Why did it start happening now?
3. Provide 2-3 different fix approaches
4. Which fix is most idiomatic for Rust?
Don’t just:
fix this error [paste entire file]
Technique: Explain the problem as if teaching someone
Effective debugging conversation:
I'm confused about this borrow checker error. Let me walk
through what I'm trying to do:
1. I have a Vector of items
2. I iterate over it and process each one
3. During processing, I need to modify the vector
4. The compiler says I can't borrow mutably while iterating
I understand why this is unsafe (iterator invalidation), but
I'm not sure of the best pattern to solve it. Should I:
- Collect items to process first, then modify?
- Use indices instead of iterators?
- Restructure to avoid modification?
What's the most idiomatic Rust approach?
Why this works: - Demonstrates you understand the problem - Shows what you’ve tried - Requests trade-offs alongside proposed solutions
Conversation pattern:
Turn 1: Get initial implementation
Create a Python function to parse CSV files with error handling
Turn 2: Add requirements
Review the first draft. Then add these missing requirements:
- Support for custom delimiters
- Type validation for numeric columns
- Progress reporting for large files
Turn 3: Optimize
This works but it's slow on my 50MB file. Can you optimize it
to process in chunks and use a streaming approach?
Turn 4: Polish
Add a NumPy-style docstring and type hints for the public interface
Turn 5: Test
Generate pytest tests covering edge cases: empty files,
malformed rows, encoding issues
Different tools for different tasks:
GitHub Copilot (in-editor): - Autocompleting functions - Generating boilerplate - Quick inline suggestions - Pattern completion
Browser chat (conversational): - Complex explanations - Architecture decisions - Debugging multi-file issues - Learning new concepts
Copilot CLI or Antigravity CLI (terminal): - Explaining error messages - Generating shell scripts - Quick command lookups - Repository analysis
Primary documentation and search: - Verifying current APIs and commands - Comparing claims with authoritative sources - Finding release notes and migration guidance
Exercise: Use AI to review this code
Prompt to try:
Review this Python function for:
1. Edge cases and error handling
2. Code clarity and maintainability
3. Potential bugs
4. More Pythonic approaches
Provide specific improvements with explanations.
Focused documentation prompt:
Draft documentation for this Python module:
[paste code]
Please create:
1. Module-level docstring explaining purpose and usage
2. Function docstrings in NumPy style
3. README section with installation and examples
4. API reference table
5. Common usage patterns section
Focus on:
- Why to use each function, along with what it does
- Executable examples using the module's documented inputs
- Edge cases and limitations
- Performance characteristics
Why this works: - Specifies format (NumPy style) - Requests usage context alongside signatures - Requests examples and edge cases - Structures output clearly
After AI generates documentation, verify:
Accuracy: - [] Parameter types match actual code - [] Return values correctly described - [] Examples actually work - [] No hallucinated features
Completeness: - [] All public functions documented - [] Edge cases mentioned - [] Error conditions explained - [] Performance characteristics noted
Clarity: - [] Written for target audience - [] Examples are practical - [] Jargon explained - [] Links to related functions
Maintenance: - [] Matches current code version - [] Breaking changes highlighted - [] Deprecation warnings included
Code to document:
Draft documentation to review:
/// Parses a single log entry from Apache Common Log Format.
///
/// This function takes a raw log line and converts it into a
/// structured `LogEntry`. It handles standard Apache CLF format
/// and returns a parsing error for malformed entries.
///
/// # Arguments
///
/// * `line` - A string slice containing a single log line
///
/// # Returns
///
/// * `Ok(LogEntry)` - Successfully parsed log entry
/// * `Err(ParseError)` - Line doesn't match expected format
///
/// # Examples
///
/// ```
/// let line = r#"127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /index.html HTTP/1.0" 200 2326"#;
/// let entry = parse_log_entry(line)?;
/// assert_eq!(entry.status_code, 200);
/// ```
///Effective refactoring prompt:
I need to refactor this code for better maintainability.
Current issues:
- 300-line function doing too much
- Duplicated error handling logic
- Hard to test individual components
- Poor separation of concerns
Goals:
- Break into smaller, focused functions
- Extract common error handling
- Make testable with dependency injection
- Maintain backward compatibility
Please:
1. Propose a refactoring strategy
2. Show the refactored structure
3. Explain the benefits of each change
4. Identify any risks or trade-offs
[paste code]
Common refactoring requests:
Extract function:
This function does validation, processing, and logging.
Extract each into separate, testable functions.
Replace magic numbers:
Replace all magic numbers with named constants at the
module level. Explain what each represents.
Introduce error types:
Convert these string error messages into a proper error enum.
Show how to use them with the ? operator.
Dependency injection:
This function directly creates a database connection. Refactor
to accept a connection as a parameter for better testing.
Simplify conditionals:
This nested if-else chain is hard to read. Refactor using
early returns or match expressions.
Focused test prompt:
Generate pytest tests for this Python function:
[paste function]
Please create tests for:
1. **Happy path**: Normal expected inputs
2. **Edge cases**: Empty inputs, None, extremes
3. **Error cases**: Invalid types, out-of-range values
4. **Boundary conditions**: Min/max values
5. **Integration**: How it works with related functions
For each test:
- Use descriptive test names
- Add docstrings explaining what's being tested
- Use pytest fixtures for common setup
- Include assertions with helpful failure messages
Result to inspect: Candidate cases that must be checked against the function contract
After AI generates tests, check:
Coverage: - [] Required behavior and important branches tested - [] Edge cases included - [] Error conditions verified - [] Integration scenarios covered
Quality: - [] Tests are independent (no shared state) - [] Meaningful test names - [] Clear assertions - [] Good failure messages
Practicality: - [] Tests actually run and pass - [] No flaky tests - [] Reasonable execution time - [] Mock external dependencies
Maintenance: - [] Tests will remain stable as code evolves - [] Not too brittle (over-specified) - [] Well-organized
Example: Git commit message generation
Workflow:
# 1. Stage only the files you reviewed
git add path/to/file.py path/to/test_file.py
# 2. Get diff
git diff --staged
# 3. Prompt AI
"Generate a conventional commit message for these changes.
Format: <type>(<scope>): <description>
Types: feat, fix, docs, refactor, test, chore
Keep description under 50 chars.
Changes:
[paste diff]"
# 4. Review and use
git commit -m "[AI-generated message]"Then give the staged diff to Copilot CLI or Antigravity CLI from inside the repository. Review the proposed message before running git commit; do not create a global alias that silently sends every staged diff to an external tool.
Custom GitHub PR workflow:
Generate a pull request description for these changes:
Branch: feature/user-authentication
Base: main
Changes made:
[paste git log]
File changes:
[paste git diff --stat]
Please create:
## Summary
- What this PR does (2-3 sentences)
## Changes
- Bullet list of main changes
## Testing
- How this was tested
## Screenshots (if applicable)
## Checklist
- [] Tests added/updated
- [] Documentation updated
- [] No breaking changes
Save as a template or shell script for reuse
When explaining complex code to teammates:
Help me explain this code to my team in our documentation:
[paste complex function]
Create an explanation that:
1. **High-level overview**: What does this do and why?
2. **Step-by-step breakdown**: Walk through the logic
3. **Key concepts**: Any patterns or techniques used?
4. **Visual diagram**: ASCII diagram if helpful
5. **Example**: Concrete input → output walkthrough
6. **Common questions**: What might teammates ask?
Audience: Junior developers familiar with Rust basics
Result to inspect: An explanation whose examples and claims can be checked against the code
Red flags in AI-generated code:
Hallucinated APIs
Outdated patterns
Over-complicated
Missing error handling
Security issues
Before using AI-generated code:
Correctness: - [] Code actually compiles/runs - [] Logic is sound - [] Edge cases handled - [] No hallucinated functions/APIs
Security: - [] Input validation present - [] No SQL injection risks - [] No command injection - [] Secrets not hardcoded
Performance: - [] Algorithms are efficient - [] No unnecessary allocations - [] Appropriate data structures - [] Scalability considered
Maintainability: - [] Code is readable - [] Well-structured - [] Properly documented - [] Follows project conventions
Tasks that often have useful drafts: - Boilerplate with an explicit template - Explanations of supplied code - Common implementation candidates - Test cases for specified behavior - Documentation outlines - Syntax translation with verification
Tasks that need stronger evidence: - Novel or context-dependent designs - Business rules absent from the prompt or repository - Performance claims without profiling - Security conclusions from a single review - Cross-file consistency in a large or partially supplied codebase - Decisions that depend on unstated project history
Response: Define the task, inspect the output, and use independent evidence to decide what to keep
Situations where AI may hurt more than help:
Learning fundamentals:
Bad: "Write a binary search tree implementation for me"
Good: "Explain how BST insertion works, then I'll implement it"
Critical security code:
Don't trust AI blindly for:
- Authentication systems
- Encryption implementation
- Permission checks
- Input sanitization
Novel problem solving:
Models often reproduce familiar patterns more reliably than they solve
novel, context-dependent problems
Production hotfixes:
Under pressure, AI mistakes are more likely. Understand
the fix before deploying.
In this course:
git diff, run the documented checks, and understand every submitted lineAGENTS.md that orients an agent and an AI_USAGE.md that honestly records how you workedThe standard: > You must be able to explain every line of code you submit, > whether you wrote it, AI wrote it, or you found it online.
Instead of: “Write this entire program for me”
Try: “I’m learning error handling in Rust. Help me understand…”
Learning-focused prompts:
I'm trying to implement [specific feature]. I've written
this code but it's not working:
[paste your attempt]
Please:
1. Don't give me the complete solution yet
2. Point out what I'm misunderstanding
3. Ask me questions to guide my thinking
4. Give me hints about which concept to study
5. Only show a solution after I've learned the concepts
Goal: Use AI as a tutor, not a solution generator
A clear workflow can demonstrate: - Efficiency: Get more done in less time - Code quality: Consistent reviews and testing - Communication: Clear documentation - Adaptability: Learning new tools/languages faster - Judgment: Knowing when to use AI vs. manual work
Questions you should be ready to answer: - “How do you use AI in your development workflow?” - “Describe a time AI helped you solve a complex problem” - “What are the limitations of AI coding assistants?” - “How do you verify AI-generated code quality?”
Patterns to evaluate in your own work: - AI for first draft, human for refinement - AI for explanations, human for decisions - AI for tedious work, human for creative work - Tests, documentation, and source code as independent evidence - Small diffs and reversible changes during iteration
Recommended workflow setup:
In-editor (GitHub Copilot): - Tab completion for code - Function generation - Quick documentation
Terminal (Copilot CLI or Antigravity CLI): - Start the agent from inside the intended repository. - Ask it to inspect a concrete error or the staged diff. - Review every proposed command and edit before approval.
Browser chat: - Architecture discussions - Complex debugging - Learning new concepts
Documentation and search: - Current authoritative instructions - Release notes and migration details - Evidence for factual or time-sensitive claims
Create a personal prompt library: - Save effective prompts you use often - Customize for your projects - Share with teammates
Prompts about prompting:
Improve your prompts:
I want to ask an AI to help me optimize this Rust code.
What information should I include in my prompt to get the
best advice?
Generate custom prompts:
Create a prompt template I can reuse for generating
focused pytest tests for Python functions. Include
placeholders for [function_code] and [special_requirements].
Learn from AI:
What are the most effective techniques for prompting AI to
help with code review? Give me 3 example prompts showing
different approaches.
Evaluate your workflow:
I'm using AI to help with [task]. Here's my current process:
[describe workflow]
How could I improve this workflow for better results?
Advanced prompting: - State the outcome, relevant context, constraints, and success criteria - Apply review criteria and examples when they communicate a real requirement - Manage context around the current task
Repeatable workflows: - AI for documentation, testing, refactoring - Build custom workflows for repetitive tasks - Verify output with independent evidence
Quality and ethics: - Understand AI limitations - Verify before using generated code - Use AI to enhance learning, not replace it - Maintain academic integrity and honest attribution
Decision making: - Choose the right tool for each task - Iterate and refine - Focus on understanding and verifiable solutions
Model guidance: - OpenAI guidance for current models - Anthropic prompting documentation
AI tools: - GitHub Copilot Documentation - Claude AI - ChatGPT - GitHub Copilot CLI - Antigravity CLI
Best practices: - Google Code Review Guidelines - Refactoring Catalog - Academic Integrity Resources
Apply these skills to your final project:
Documentation: - [] Draft and verify a complete README - [] Add docstrings where they clarify public behavior - [] Create usage examples - [] Document installation steps
Code quality: - [] AI-assisted code review - [] Refactor complex functions - [] Add tests for required behavior, failure paths, and important boundaries - [] Check for security issues
Repository polish: - [] Write clear commit messages - [] Create detailed PR descriptions (if applicable) - [] Add comments where the reason for a choice is not evident from the code - [] Include performance notes only when they are measured and relevant
Use AI where it improves a rubric requirement, and verify every resulting change.
Get help:
Follow the final-project deadline shown on the course site and in Canvas.
Remember: tool output is a proposal. You remain responsible for the requirements, evidence, and submitted result.
You have reached the final project stage.
Throughout this course, you’ve learned: - Python and Rust programming - Repeatable development practices - Testing, CI/CD, and distribution - Evidence-based AI-enhanced development workflows
What’s next: - Complete the final project against the published rubric - Publish work only when its configuration and data are safe to share - Continue practicing AI-enhanced development - Apply the same verification habits in future projects
Finish the final project by testing the behavior, reviewing the diff, and checking every rubric item.
IS 4010: App Dev with AI