Skip to content

Instantly share code, notes, and snippets.

@pedroduartecosta
Last active February 11, 2025 13:04
Show Gist options
  • Save pedroduartecosta/74807da491dbfd246f4a83a9029192e9 to your computer and use it in GitHub Desktop.
Save pedroduartecosta/74807da491dbfd246f4a83a9029192e9 to your computer and use it in GitHub Desktop.
How I Code with AI: A Practical Guide

How I Code with AI: A Practical Guide

Over the past three years, I've integrated AI tools into almost every aspect of my development workflow. From migrating legacy bash scripts to TypeScript CLIs, building Next.js applications, writing backend and frontend TypeScript code, creating Helm charts, managing infrastructure with Terraform, to debugging code across multiple languages - AI has become an essential part of how I work. I've used it for everything from improving complex SQL queries to writing Python scripts, and even for non-coding tasks like drafting postmortems and technical reviews. Here's what I've learned along the way about making AI tools truly useful in real-world development.

The Foundation: Choose the Right Tools

I primarily use two AI tools for coding:

  1. Claude Pro: My main tool for complex coding tasks and project work. While there are many AI coding assistants available, I've found Claude to be the most reliable and capable of understanding complex contexts without veering off course. The investment in the pro version has paid off in terms of consistency and capabilities.

  2. GitHub Copilot: My go-to tool while actively coding. It serves two primary purposes in my workflow:

    • As an intelligent autocomplete tool that significantly speeds up my coding process
    • For quick fixes to code issues or minor errors in Claude-generated code
    • When I'm feeling lazy, I can write a comment describing what I want, and Copilot will generate the code inline

This combination of tools provides a powerful workflow where Claude handles the heavy lifting of complex tasks, while Copilot helps with the minute-to-minute coding process.

The Product Owner and Code Reviewer Mindset

One of the most important aspects of my workflow is how I position myself when working with AI. Rather than simply copying and pasting code, I approach the relationship in two key roles:

  1. Product Owner: I clearly define requirements, constraints, and expectations. The AI serves as the developer implementing these requirements.

  2. Code Reviewer: This is perhaps the most crucial role. Just like with human developers, code review makes all the difference. I carefully review AI-generated code for:

    • Package choices and dependencies
    • Redundant or unnecessary code
    • Performance implications
    • Security considerations
    • Edge cases and error handling
    • Consistency with existing codebase
    • Best practices and patterns

This approach ensures that while AI accelerates development, we maintain high code quality and thoughtful architecture decisions.

Context is King: Smart Ways to Share Your Codebase

One of the most crucial aspects of working with AI is providing the right context. I use two main commands for this purpose:

Project Structure Context

tree --gitignore | pbcopy

This command creates a tree representation of your project while respecting .gitignore rules. Here's how to customize it:

  • Remove --gitignore if you want to see all files, including ignored ones
  • In macos pbcopy copies the output directly to your clipboard.

Code Context

find . -type f -not -path '*/\.*' -not -path '*/public/*' -print0 2>/dev/null | \
grep -vzZ -- '-lock\.yaml$' | \
xargs -0 sh -c 'for f do git check-ignore -q "$f" || (echo "=== $f:" && cat "$f"); done' sh | pbcopy

This command compiles all your relevant code files into a single text output, making it easy to share with AI. It:

  1. Finds all files in your project
  2. Outputs each file's content with its filename and path as a header (this is really important for AI to understand the context)
  3. Excludes hidden files, public directories, and lock files
  4. Checks each file against git ignore rules
  5. Copies everything to your clipboard

Here's how to modify it:

  1. find . -type f: Finds all files in the current directory and subdirectories

    • You can specify a different path instead of . to search in a specific directory
    • Add -maxdepth N to limit directory depth
  2. -not -path patterns: Excludes specific paths

    • Add more patterns with additional -not -path arguments
    • Common patterns to exclude: node_modules, dist, build
  3. grep -vzZ -- '-lock\.yaml$': Excludes lock files

    • Modify the pattern to exclude different file types
    • Add multiple patterns with \|

The command is particularly useful in two scenarios:

  1. When iterating on existing code
  2. When creating new features that should maintain consistency with existing patterns

However, there's an important caveat: be mindful of context window limitations. For instance, when working on a new page in a Next.js website, you don't need to share the entire codebase. Instead, focus on sharing similar components or pages that can serve as style and architecture references.

Brainstorming with AI

AI has become an invaluable brainstorming partner. Here's how I leverage it:

Complex Task Planning

When facing complex tasks, I start by:

  1. Writing out my initial thoughts
  2. Listing potential challenges and issues
  3. Identifying possible tools and approaches
  4. Using AI to discuss pros and cons of different approaches
  5. Exploring edge cases and potential pitfalls

Performance Optimization

AI is excellent for brainstorming performance improvements. I can discuss:

  • Different optimization strategies
  • Tradeoffs between approaches
  • Potential bottlenecks
  • Scalability considerations

The Art of Iteration

A key lesson I've learned is that AI works best with incremental development. Here's my approach:

  1. Start with Structure: When beginning a new project, I first ask the AI to create a shell script that will generate the project structure and files. This gives us a solid foundation to build upon.

  2. File-by-File Development: Rather than trying to generate an entire application at once, I work on one file at a time. This approach leads to better quality code and fewer errors.

  3. Fresh Conversations: I start new conversations when the context gets too heavy. Long conversations can lead to degraded results, so I regularly begin fresh with updated context.

Moving at Bullet Train Speed: Real-World Example

The real power of AI is in how it helps catch and solve problems early while maintaining rapid development speed. Here's a concrete example:

Rate-Limited Email Service in a Monorepo

Recently, I needed to add an email service library to a monorepo that would be used across different parts of the application:

  • Website reporting features
  • Cron jobs
  • Various other services

The constraint was simple: AWS SES limited us to 14 emails per second. Instead of building a complex solution from scratch, I shared my existing email sending code with the AI and asked it to incorporate rate limiting. The AI quickly implemented a token bucket algorithm and suggested future improvements like using Redis for distributed rate limiting, adding retry mechanisms, and implementing queue systems for high-load scenarios.

This is a perfect example of how AI accelerates development:

  • Quick implementation of immediate needs
  • Thoughtful suggestions for future scaling
  • Early identification of potential issues

Working Within AI Limitations

Understanding AI's limitations is crucial for effective collaboration. For instance, Claude typically can generate around 400 lines of code at a time. When you need more, a simple "Continue writing" prompt usually works. If it loses context, don't hesitate to go back and edit your message or try again.

Beyond Code Generation

While this guide focuses on coding workflows, it's worth noting that AI can enhance many other aspects of software development. I use it for various tasks like:

  • Writing postmortems based on incident conversations
  • Analyzing performance bottlenecks
  • Reviewing configuration changes
  • Documenting complex systems

The possibilities are vast, and I encourage you to experiment and discover how AI can best serve your specific needs and workflow. The examples I've shared are just a starting point – the real power comes from finding your own ways to leverage these tools effectively.

The Power of Early Problem Solving

One of the biggest advantages of coding with AI is how it helps identify and solve problems early in the development cycle. You can:

  • Quickly prototype different approaches
  • Test edge cases and limitations
  • Explore scaling considerations
  • Implement best practices from the start

Instead of building something basic and iterating later, AI helps you think through and implement robust solutions from the beginning, while still moving at incredible speed.

Key Takeaways

  1. Tool Selection: Use different AI tools for different purposes - Claude for complex tasks, Copilot for active coding.
  2. Smart Context: Share context intelligently, focusing on what's relevant rather than dumping entire codebases.
  3. Incremental Development: Break down development into smaller, manageable chunks.
  4. Regular Refresh: Don't hesitate to start fresh conversations when needed.
  5. Understand Limitations: Work within the AI's capabilities rather than fighting against them.
  6. Hybrid Approach: Combine AI assistance with traditional coding for the best results.
  7. Review Mindset: Always review AI-generated code with the same rigor you'd apply to human-written code.
  8. Brainstorming Partner: Use AI for planning and problem-solving, not just code generation.
  9. Early Optimization: Leverage AI to implement robust solutions from the start.

By following these practices, I've found that AI becomes a powerful ally in development rather than a source of frustration. The key is to treat it as a sophisticated tool that requires proper handling rather than a magical solution that will write your entire application in one go.

Remember, the goal isn't to replace traditional coding practices but to enhance them. When used correctly, AI can significantly speed up development while maintaining code quality and consistency.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment