2.1. Rule 4: Start by Thinking Through a Potential Solution#

Begin AI coding sessions by first working to understand and articulate the problem you’re trying to solve, specified at a level of abstraction that makes it solvable by code, and think through how you anticipate it might be solved. Think through the entire problem space: What are the inputs and expected outputs? What are the key constraints and edge cases? What does success look like? This problem-focused approach serves a dual purpose: it helps you clarify exactly what you want the AI to accomplish so that you can evaluate its outputs appropriately, and it prevents the AI from making incorrect assumptions about your goals. When you provide problem context along with architectural details of how you anticipate a solution working (i.e., how it might fit in with the “bigger picture”), the AI generates code that fits naturally into your project rather than creating isolated solutions. Include details about data flow, component interactions, and expected interfaces when possible or if relevant. This approach transforms the AI from a code generator into an architecture-aware development partner. You can use LLMs to help generate externally-managed context files, and also look at GitHub Spec Kit for specification-driven workflows that define project requirements and gated phases (Specify, Plan, Tasks). AI can help you implement sophisticated patterns like structured checklists for iterative development that would be tedious to write from scratch.

2.1.1. What separates positive from flawed examples#

Flawed examples dump vague requirements on the AI without any planning. You haven’t thought through how the pieces should fit together, what the data flow looks like, or what success actually means. The AI fills in the gaps with its own assumptions, which are probably wrong for your use case. You end up with code that technically does something but doesn’t integrate well with your existing system.

Positive examples start with upfront planning. You’ve thought through the problem structure, anticipated how it should be solved, and specified the architectural context. You can articulate inputs, outputs, data flow, and how this component interacts with others. This gives the AI enough structure to generate code that actually fits your needs rather than creating a standalone solution that requires major rework.


2.1.1.1. Example 1: Vague Requirements Without Planning#

The user hasn’t thought through what they actually need. No specification of how this integrates with existing code. No clarity on data flow or expected behavior. The AI has to guess at everything (data format, processing steps, output structure). What comes back is generic code that probably doesn’t match the actual system architecture. When the user tries to integrate this, they’ll discover all sorts of mismatches they should have specified upfront: the function expects a file path but the existing DataLoader returns a dictionary; the return type is ambiguous (NIfTI object? numpy array? dictionary?); there’s no error handling for missing files or incompatible data; and the validation function is completely arbitrary because the user never specified what valid data looks like. The “modular” request gets interpreted as separate functions, but with no clear interfaces or type specifications, making them impossible to test or reuse independently.


2.1.1.2. Example 2: Upfront Architecture Specification#

The user has done the planning work before asking for code. They specify the complete system architecture with clear class boundaries and responsibilities. They define the exact API for each component including input/output types, error conditions, and side effects. They explain how data flows through the system and what each transformation does. They specify which parts are already implemented versus what needs to be created. This level of detail ensures the AI understands not just what to do, but how it should fit with existing code patterns and design. The resulting implementation can actually be dropped into the codebase because it matches the existing architecture.


2.1.1.3. Example 3: Iterative Refinement with Clear Goals#

The user starts with a basic specification to establish the core functionality. Once that works, they add complexity through well-defined iterations. Each iteration has clear success criteria and builds naturally on the previous version. The user can verify each iteration independently before moving to the next. This prevents the AI from trying to solve everything at once and making wrong assumptions about priorities. The progressive refinement keeps the conversation focused and makes debugging much easier.