Directing AI: A Masterclass in Software Design and Code Refactoring

ArjanCodes////4 min read

Overview

Most developers treat AI as a magic wand that spits out finished applications in minutes. This mindset creates a significant long-term problem: unmanageable complexity. When you ask an AI to "build a dashboard," it often generates a monolithic block of code that works initially but breaks the moment you need to scale or modify it. Proper software design is your primary tool for managing this complexity. By applying design principles, you reduce the cognitive load on the AI, making your prompts clearer and the resulting code more maintainable. This tutorial explores how to guide an AI coding assistant like ChatGPT through the iterative process of building a robust animation system in Python.

Prerequisites

To follow this guide, you should be comfortable with basic Python syntax and object-oriented programming. Familiarity with Python or Python is helpful. You should also understand the concept of a "loop" in the context of graphics, specifically how a canvas updates over time.

Directing AI: A Masterclass in Software Design and Code Refactoring
The Right Way to Use AI for Writing Maintainable Code

Key Libraries & Tools

  • Python 3.10+: Uses modern type annotations and lowercase collection types.
  • Python: The standard GUI library used here for canvas rendering.
  • ChatGPT: The LLM used to generate and refactor code iterations.
  • Python: A built-in module for providing type hints and protocols.

Code Walkthrough

1. Defining the Animation Protocol

We start by decoupling the animation logic from the runner. Instead of a massive if statement checking for "move" or "rotate," we define a protocol. This ensures every animation step follows a predictable structure.

from typing import Protocol

class AnimationStep(Protocol):
    def apply(self, shape_id: int, renderer: "GraphicsRenderer", t: float) -> None:
        ...

2. Implementing Decoupled Commands

By turning each action into its own class (the Command Pattern), we make the system extensible. Notice how MoveStep doesn't know about the internal state of the Animator; it simply receives what it needs to perform its specific transformation.

from dataclasses import dataclass

@dataclass
class MoveStep:
    start_pos: tuple[float, float]
    end_pos: tuple[float, float]

    def apply(self, shape_id: int, renderer: "GraphicsRenderer", t: float) -> None:
        # Interpolate between start and end based on time t
        curr_x = self.start_pos[0] + (self.end_pos[0] - self.start_pos[1]) * t
        # ... apply to renderer

3. Separation of Concerns: The Renderer

A common AI mistake is giving one class too many jobs. Initially, the Animator handled the canvas, the shapes, and the timing. We refactor this by creating a GraphicsRenderer that only cares about low-level operations: points and colors.

class GraphicsRenderer:
    def __init__(self, canvas):
        self.canvas = canvas
        self.items = {}

    def render(self, shape_id: int, points: list[float], color: str):
        if shape_id not in self.items:
            self.items[shape_id] = self.canvas.create_polygon(points, fill=color)
        else:
            self.canvas.coords(self.items[shape_id], *points)
            self.canvas.itemconfig(self.items[shape_id], fill=color)

4. Refining the Playback Logic

The final hurdle involves preventing cumulative errors. If you add to a shape's position every frame, the shape will eventually fly off the screen. We solve this by storing an "original state" at the start of the animation and calculating every frame relative to that baseline.

Syntax Notes

  • Lower-case Types: Use list[int] and dict[str, int] instead of the deprecated uppercase List and Dict from the typing module.
  • Protocols vs. ABCs: While Python work for inheritance, Protocols allow for structural subtyping (duck typing), which is often cleaner for animation steps.
  • Dependency Injection: Notice that the GraphicsRenderer accepts a canvas in its constructor. This makes the code easier to test and more flexible.

Practical Examples

This design approach is essential for any system where behavior changes over time. Beyond simple animations, you can apply these principles to:

  • Data Pipelines: Treating each processing step as a command.
  • Game Development: Separating entity logic from the rendering engine.
  • UI Frameworks: Decoupling event handling from the visual representation.

Tips & Gotchas

  • Circular Dependencies: Watch out for classes that require each other (e.g., Animator needing Step while Step needs Animator). Solve this by using abstractions or moving methods to where the data lives.
  • AI Context Drift: LLMs often "forget" your previous design constraints, like lowercase type hints. You must be prepared to correct them multiple times.
  • Cumulative Mutations: Always prefer calculating state from a fixed starting point rather than adding small increments. Incremental updates lead to "drift" due to floating-point math errors.
Topic DensityMention share of the most discussed topics · 13 mentions across 5 distinct topics
Python
62%· products
ChatGPT
15%· products
Arjan
8%· people
VS Code
8%· products
End of Article
Source video
Directing AI: A Masterclass in Software Design and Code Refactoring

The Right Way to Use AI for Writing Maintainable Code

Watch

ArjanCodes // 27:34

On this channel, I post videos about programming and software design to help you take your coding skills to the next level. I'm an entrepreneur and a university lecturer in computer science, with more than 20 years of experience in software development and design. If you're a software developer and you want to improve your development skills, and learn more about programming in general, make sure to subscribe for helpful videos. I post a video here every Friday. If you have any suggestion for a topic you'd like me to cover, just leave a comment on any of my videos and I'll take it under consideration. Thanks for watching!

What they talk about
AI and Agentic Coding News
Who and what they mention most
Python
27.3%3
Python
18.2%2
Python
18.2%2
4 min read0%
4 min read