Critical Thinking Habits for Coding with AI

Introducing the Sens-AI Framework

Andrew Stellman

Critical Thinking Habits for Coding with AI

by Andrew Stellman

Copyright © 2026 O’Reilly Media, Inc. All rights reserved.

Published by O’Reilly Media, Inc., 141 Stony Circle, Suite 195, Santa Rosa, CA 95401.

O’Reilly books may be purchased for educational, business, or sales promotional use. Online editions are also available for most titles (https://oreilly.com). For more information, contact our corporate/institutional sales department: 800-998-9938 or corporate@oreilly.com.

Acquisitions Editor: Louise Corrigan

Development Editor: Sara Hunter

Production Editor: Christopher Faucher

Copyeditor: Piper Content Partners

Proofreader: O’Reilly Media, Inc.

Cover Designer: Ellie Volckhausen

Interior Designer: David Futato

Interior Illustrator: Kate Dullea

November 2025: First Edition

Revision History for the First Edition

2025-10-24: First Release

The O’Reilly logo is a registered trademark of O’Reilly Media, Inc. Critical Thinking Habits for Coding with AI, the cover image, and related trade dress are trademarks of O’Reilly Media, Inc.

The views expressed in this work are those of the author and do not represent the publisher’s views. While the publisher and the author have used good faith efforts to ensure that the information and instructions contained in this work are accurate, the publisher and the author disclaim all responsibility for errors or omissions, including without limitation responsibility for damages resulting from the use of or reliance on this work. Use of the information and instructions contained in this work is at your own risk. If any code samples or other technology this work contains or describes is subject to open source licenses or the intellectual property rights of others, it is your responsibility to ensure that your use thereof complies with such licenses and/or rights.

979-8-341-66630-6

[LSI]

The Sens-AI Framework: Critical Thinking Habits for Coding with AI

AI has fundamentally changed how we write code. AI-assisted coding tools like GitHub Copilot and Cursor can dramatically speed up development, helping developers generate working code faster than ever before, and help developers work with unfamiliar technologies and frameworks, making it easier to explore new approaches without getting stuck on syntax or boilerplate. These tools have even introduced an entirely new style of development that’s become known as “vibe coding,” a rapid, improvisational approach that helps developers explore ideas and iterate quickly that can benefit both new and seasoned developers when used well.

But AI-assisted coding comes with real drawbacks. Developers who rely solely on AI risk skipping deliberate design work, introducing subtle bugs that are hard to catch, and accelerating technical debt that makes codebases harder to maintain over time. New developers face a particular challenge: the “cognitive shortcut paradox” where AI tools prevent them from building the critical thinking skills needed to use those same tools effectively. And there’s growing evidence that AI tools aren’t always delivering on their promises—many developers report spending more time debugging AI-generated code than they would have spent writing it themselves.

These challenges led me to develop a new approach to teaching and using AI in software development.

The Sens-AI Framework provides a structured, flexible approach for learning and teaching responsible and effective AI practices without undermining core software development skills. It introduces five practical habits—Context, Research, Problem Framing, Refining, and Critical Thinking—developed through real teaching experience in Head First C#, live workshops, and corporate training. These habits help teams recognize warning signals like rehash loops, improve the clarity of their prompts, and actively review AI-generated code to maintain quality and design intent. The goal is to help developers move beyond the frustrating cycles of debugging AI mistakes and instead build a sustainable, productive partnership with these tools.

This report explores how AI is reshaping developer career paths, shifting value from deep specialists to generalists who can integrate across systems and spot design problems early. It connects modern prompt engineering to 50 years of software engineering lessons, showing how today’s AI challenges mirror the requirements engineering problems teams have grappled with since the 1960s. The report also examines maintainability risks at the codebase level, such as technical debt and fragile architectures, alongside the importance of “trust but verify” as a teaching and review principle.

Beyond these technical and strategic considerations, the report addresses the human side of AI-assisted coding. This includes the habits, frustrations, and teaching strategies that help developers at every experience level build confidence and avoid common pitfalls. Real-world case studies, practical checklists, and coaching prompts help instructors, team leads, and professional developers guide their teams in adopting AI thoughtfully, combining its speed with the judgment and design thinking needed to build quality software.

Introduction: The Promise—and the Trap—of AI Coding Assistants

AI coding assistants aren’t optional anymore at an increasing number of companies. These tools, like GitHub Copilot, ChatGPT, and Claude, can be remarkably good at generating, completing, and refactoring code based on natural language descriptions or existing code context. But they’re increasingly being pushed as required tools for faster development and higher productivity, and that creates real pressure for developers at all experience levels.

For many developers, myself included, those promised productivity gains are real. These AI tools can help speed up routine work, encourage exploration of new approaches, and generate ideas that might not have come up otherwise (Laster 2025). But I’ve also seen plenty of frustration, especially among early-career developers who aren’t getting consistent results and feel left behind.

However, there’s growing evidence that AI tools aren’t delivering on their promises (Vizard 2025). Developers report spending more time debugging AI-generated code than writing it themselves. Teams are discovering that what seemed like rapid progress turns into technical debt that takes weeks to untangle. This systematic problem shows up when developers try to move from demos, research, and exploratory projects to real production work, where maintainability and design actually matter.

The shift to AI-assisted coding creates real pressure for developers at all experience levels. Organizations are demanding measurable productivity gains from AI adoption, but it’s not always clear how to deliver those gains reliably, especially as projects get more complex. The gap between what works in polished demos and what’s needed in real project work can be frustrating and professionally risky.

AI coding tools are driven by natural language prompts, which adds an entirely new skill set that developers must learn to do their jobs. Prompt engineering is the practice of writing clear, structured inputs that guide an AI tool to generate the output you want (Phoenix and Taylor 2024), and it’s become a critical skill for using AI tools. Communicating clearly in code is not the same thing as communicating clearly in writing, and many developers are looking for new and effective ways to pick up these skills.

AI-assisted coding tools really do work, and you can get good results with them. In my O’Reilly training courses, I give students exercises like building a math quiz game with Copilot, refactoring a class, or writing a unit test, and I’ve seen them do it well. When I demonstrate those same tasks in training sessions with Copilot, ChatGPT, Claude, or other AI tools, it usually works very well, too, even if live AI demos sometimes have the occasional hiccup. But once you move from these controlled examples to real project work, it suddenly feels much harder, and it’s not always clear why. That gap is where most developers fall into “vibe coding.”

Vibe coding is an exploratory, prompt-first approach to software development where developers rapidly prompt, get code, and iterate. When the code seems close but not quite right, the developer describes what’s wrong and lets the AI try again. When it doesn’t compile or tests fail, they copy the error messages back to the AI. The cycle continues: prompt, run, error, paste, prompt again, often without reading or understanding the generated code. It feels productive—and it often is—because you’re making visible progress: errors disappear, tests start passing, features seem to work. You’re essentially treating the AI like a coding partner who handles the implementation details while you steer at a high level (Osmani 2025).

Developers use vibe coding to explore and refine ideas and can generate large amounts of code quickly. It’s often the natural first step for most developers using AI tools, because it feels so intuitive and productive. Vibe coding offloads detail to the AI, making exploration and ideation fast and effective, which is exactly why it’s so popular.

But because vibe coding leans on the AI’s “understanding” instead of our own, it’s easy for a developer to disengage and let the AI do the work without really thinking through the design. Knowing when to slow down, review, and stay engaged can be a challenge, even for experienced developers.

I’ve observed additional challenges in practice. When AI generates large volumes of code quickly, comprehensive review becomes impractical. The vibe coding loop can be an efficient way to use AI, but it makes it easy for developers to lose track of what the code really does. For new and early-career developers, this can create a critical skill-building gap: they may miss learning essential practices like readability, separation of concerns, design patterns, architecture, and more generally, getting a feel for writing and reading code. That gap is increasingly problematic for developers, because those are exactly the critical thinking skills they need to evaluate AI output effectively. Even experienced developers can slip into habits that seem productive but create problems down the line, like accepting AI suggestions without questioning them, skipping design discussions, or pushing unreviewed code too quickly.

I see challenges to using AI show up repeatedly in my own development work, as well as in my training and coaching sessions. Those experiences led me to design the Sens-AI Framework. It consists of five structured habits that help developers know when to switch from rapid generation to careful review, keep critical thinking active during AI collaboration, and preserve the design judgment that effective engineers rely on every day. The framework also offers a clear learning path for newer developers, helping them build these essential habits alongside their core coding skills.

When Vibe Coding Breaks Down

Vibe coding has genuine appeal because it makes exploring ideas fast and easy. Instead of writing every line manually, you can describe what you want and quickly get working code to refine. It offloads implementation details from your brain to the AI, making it easier to try variations, test designs, or overcome blank-page paralysis. This improvisational style feels fluid and natural when it works, which is why it’s so popular with teams looking to boost development speed and creativity.

Vibe coding’s strengths can also make it easy to miss several specific ways that AI can fail:

Hallucination occurs when AI generates plausible-looking but flawed code. Hallucination can be especially problematic when it generates code that seems to work, but has easily missed problems like methods that compile but hide subtle logic errors or ignore edge cases.

Overconfidence shows up when developers treat AI suggestions as correct without the usual validation, testing, or design review. These risks are well known, but they can still trip up experienced developers, especially under delivery pressure. They routinely show up in real AI-assisted coding sessions and can be surprisingly consistent.

Rehash loops happen when developers keep tweaking prompts and the AI keeps returning slight variations of the same flawed solution. Instead of moving forward, the process stalls with the AI circling around the same few ideas or solutions.

I rely on vibe coding extensively in both my teaching and my own development work. It genuinely helps me generate high-quality code quickly and has accelerated many projects. But using it successfully means regularly reviewing the generated code, watching for signs of technical debt, or the accumulated shortcuts and compromises that make code harder to maintain. Common examples of technical debt include tightly coupled modules that can’t be changed independently, duplicated logic scattered across files, or overly complex abstractions that obscure simple operations. Developers typically apply solid, human-oriented practices like unit testing, careful review, and design discipline to prevent technical debt from accumulating and the code clean and maintainable.

Vibe coding is a normal and useful way to explore with AI, but despite its appeal and the fact that it can be effective, it presents a significant risk. The models used by large language models (LLMs) can hallucinate and produce made-up answers, like generating code that calls APIs or methods that don’t even exist. Preventing those AI-generated mistakes from compromising your codebase starts with understanding the capabilities and limitations of these tools, and taking an approach to AI-assisted development that takes those limitations into account.

Here’s a simple example of how these issues compound. When I ask AI to generate a class that handles user interaction, it often creates methods that directly read from and write to the console. When I then ask it to make the code more testable, if I don’t very specifically prompt for a simple fix like having methods take input as parameters and return output as values, the AI frequently suggests wrapping the entire I/O mechanism in an abstraction layer. Now I have an interface, an implementation, mock objects for testing, and dependency injection throughout. What started as a straightforward class has become a miniature framework. The AI isn’t wrong, exactly. The abstraction approach is a valid pattern, but it’s overengineered for the problem at hand. Each iteration adds more complexity, and if you’re not paying attention, you’ll end up with layers upon layers of unnecessary code. This is a good example of how vibe coding can balloon into unnecessary complexity if you don’t stop to verify what’s happening.

The signals of AI-accelerated technical debt show up quickly: highly coupled code where modules depend on each other’s internal details; “God objects” with too many responsibilities; overly structured solutions where a simple problem gets buried under extra layers. These are the same problems that typically reflect technical debt in human-built code; the reason they emerge so quickly in AI-generated code is because it can be generated much more quickly and without oversight or intentional design or architectural decisions being made. AI can generate these patterns convincingly, making them look deliberate even when they emerged by accident. Because the output compiles, passes tests, and works as expected, it’s easy to accept as “done” without thinking about how it will hold up when requirements change.

Left to its defaults, AI-assisted development is biased toward adding new code, not revisiting old decisions. That’s likely because language models are trained to generate responses that look new and complete; they aren’t necessarily optimized to ask whether existing code should be refactored.

When developers add features, they usually work with an understanding of the existing architectural patterns, helper functions, and design philosophy. An AI, however, doesn’t have that holistic view. It’s trained on a massive dataset of code, but when generating a solution it’s focused mainly on the immediate task. That tight focus often leads to common code generation failures:

Duplication

The AI generates a function that repeats the logic of an existing one. This makes the codebase larger and harder to maintain, and if a bug is found in one version, it has to be fixed everywhere it appears.

Lack of integration

The AI creates new methods, functions, classes, or modules instead of extending existing ones. This misses chances to apply design patterns and principles, resulting in a less cohesive design that becomes rigid and harder to extend.

Over-abstraction

The AI generates extra layers of classes, interfaces, or helper functions that don’t add real value. This increases complexity, makes the code harder to follow, and over time creates the same rigidity and maintenance headaches as duplication or poor integration.

The discipline to avoid technical debt comes from building design checks into your workflow so AI’s speed works in service of maintainability instead of against it. Preventing technical debt has always relied on developers spotting and fixing antipatterns before they harden into long-term problems. Future iterations of AI models may get better at suggesting refactoring or reuse, but the bias toward producing fresh output will remain. That’s why developers need deliberate checkpoints to ensure the codebase grows responsibly rather than haphazardly.

Understanding the Rehash Loop

While hallucination and overconfidence get most of the attention, there’s another failure mode that’s equally common but less well understood. The rehash loop is a failure mode in AI-assisted development characterized by repeated prompt-and-response cycles that produce variations without meaningfully addressing the underlying problem. Developers can recognize they’re in a rehash loop when the AI keeps responding with the same core idea or limited set of solutions in slightly different forms, and prompt adjustments seem to make no real progress toward a workable solution. The typical developer response is to try escaping through incremental changes: adding details, rewording instructions, or nudging the AI toward a fix. But these adjustments, which are typically effective during normal vibe coding sessions, still lead back to the same constrained set of answers.

Rehash loops are a frustrating problem because progress stalls with no clear path to a solution, or even something new that could potentially lead to one. Even more challenging is the uncertainty: developers can’t tell if there’s no real solution and the work has hit a genuine dead end, if the question is being asked the wrong way, or if the AI is hallucinating a partial answer and overconfident that it works. The ambiguity created by the rehash loop can leave developers feeling stuck, questioning what to try next or whether the problem is even solvable.

When you’re in a rehash loop, the AI isn’t broken or hallucinating. It’s doing exactly what it’s designed to do: generating the most statistically likely response it can, based on the tokens in your prompt and the limited view it has of the conversation. One source of the problem is the context window: a fixed-size buffer that determines how much text the model can “see” at once. That includes your prompt, any shared code, and the rest of the conversation, usually a few thousand tokens in total. Once the model has sampled the patterns it finds there, it starts circling.

The variations you get, like reordered statements, renamed variables, a tweak here or there, aren’t new ideas. They’re just the model nudging things around in the same narrow probability space. It’s still pointing at the same chunk of its training data.

So if you keep getting the same broken answer, the issue probably isn’t that the model doesn’t know how to help. It’s that you haven’t given it enough to work with.

To understand why rehash loops happen, and why AI can get stuck repeating partial answers, it helps to take a closer look at how large language models work:

LLMs don’t really understand code the way humans do. They generate suggestions by predicting what comes next in a sequence of text based on patterns they’ve seen in massive training datasets (Taulli 2024). When you prompt them, they analyze your input and predict likely continuations, but they have no real understanding of your design or requirements unless you explicitly provide that context.

Context is the information the AI uses to decide what to generate next. It includes everything you’ve told it in the prompt, recent conversation, and even the surrounding code in your project. The better context you provide, the more useful and accurate the AI’s answers will be.

LLMs don’t understand design intent or architecture; they just try to predict the next most likely text based on the context window, a limited chunk of text that includes the prompt, conversation history, and surrounding code that the AI can “see” at any given time. This is why they can produce helpful completions, unit tests, or even bug fixes, but also hallucinate APIs that don’t exist or generate code with subtle design flaws (Phoenix and Taylor 2024). When the context is incomplete or poorly framed, the AI’s suggestions can drift, repeat variations, or miss the real problem entirely.

While rehash loops seem like a frustrating problem, developers can also use them positively by recognizing that a rehash loop is a signal that the AI ran out of context. When you’re stuck in a rehash loop, treat it as a signal instead of a problem. All you need to do to break out of the loop and resume productive use of the AI is figure out what context is missing and provide it.

AI researchers have studied related phenomena like “repetitive generation” (Doan 2023) or “prompt saturation” (Milios 2023) at the model level, but those terms don’t capture the developer experience of hitting this wall. By clearly naming the rehash loop pattern, we can better recognize when it happens and treat it as a meaningful signal to step back and rethink our approach.

The Cognitive Shortcut Paradox

Generative AI can be a powerful learning tool for developers at every experience level, but it poses hidden dangers for early-career developers. For experienced developers, that can mean getting to a working solution faster. Developers early in their learning path, however, face what I call the cognitive shortcut paradox: they need coding experience to use AI tools well, because experience builds the judgment required to evaluate, debug, and improve AI-generated code, but leaning on AI too much in those first stages can keep them from ever gaining that experience.

I saw this firsthand when adapting my book Head First C# to include AI exercises (Stellman and Greene 2024). The book’s exercises are built to teach specific development concepts like object-oriented programming, separation of concerns, and refactoring. If new learners let AI generate the code before they’ve learned the fundamentals, they miss the problem-solving work that leads to those “aha!” moments where understanding really clicks.

With AI, it’s easy for new learners to bypass the learning process completely by pasting the exercise instructions into a coding assistant, getting a complete program in seconds, and running it without ever working through the design or debugging. When the AI produces the right output, it feels like progress to the learner. But the goal was never just to have a running program; it was to understand the requirements and craft a solution that reinforced a specific concept or technique that was taught earlier in the book. The problem is that their work still looks right. The code compiles and produces the expected results, so the missing skills stay hidden until the gap is too wide to close.

Evidence is emerging that AI chatbots can boost productivity for experienced workers but have little measurable impact on skill growth for beginners (Humlum and Vestergaard 2025). In practice, AI coding assistants can cause growing learning gaps for early-career developers, because they hand over a polished answer before they’ve had the chance to build the skills needed to use that answer effectively.

The cognitive shortcut paradox isn’t just a classroom issue. In real projects, the most valuable engineering work often involves understanding ambiguous requirements, making architectural calls when nothing is certain, and tracking down the kind of bugs that don’t have obvious fixes (Stellman and Greene 2005). Those abilities come from wrestling with problems that don’t have a quick path to “done.” If developers turn to AI at the first sign of difficulty, they skip the work that builds the pattern recognition and systematic thinking senior engineers depend on.

Over time, the effect compounds. A new developer might complete early tickets through vibe coding, feel the satisfaction of shipping working code, and gain confidence in their abilities. Months later, when they’re asked to debug a complex system or refactor code they didn’t write, the gap shows. By then, their entire approach to development may depend on AI to fill in every missing piece, making it much harder to develop independent problem-solving skills.

The cognitive shortcut paradox presents a fundamental challenge for how we teach and learn programming in the AI era. The traditional path of building skills through struggle and iteration hasn’t become obsolete; it’s become more critical than ever, because those same skills are what allow developers to use AI tools effectively. The question isn’t whether to use AI in learning, but how to use it in ways that build rather than bypass the critical thinking abilities that separate effective developers from code generators. This requires a more deliberate approach to AI-assisted development, one that preserves the essential learning experiences while harnessing AI’s capabilities.

Introducing the Sens-AI Framework

The Sens-AI Framework is a structured, teachable approach for software developers to learn AI skills while addressing a dual challenge in AI-assisted development. For new developers, it provides a way to use AI without treating it as a crutch, ensuring they build the critical thinking skills needed to design, analyze, and understand code. For all developers, including experienced professionals, it helps maintain design judgment and code quality when working with AI tools.

The framework also serves several practical purposes: transforming abstract advice into repeatable habits; helping developers recognize frustration signals like rehash loops as cues to shift strategies; and supporting team development by giving organizations a shared language for discussing effective AI practices.

The Sens-AI Framework is built around five structured habits that help developers collaborate effectively with AI while preserving the critical thinking and design judgment that distinguish good software engineering. These habits don’t form a linear process or a step-by-step recipe. Instead, the framework is a set of flexible habits that developers can apply as needed to guide their work and recognize when to shift modes:

Context

Providing the AI with the right background, requirements, and constraints. Ensure prompts provide the necessary context for the AI.

Research

Investigating the problem space before prompting. Do additional research when initial attempts feel shallow or incomplete, or run into problems, like when you hit a rehash loop.

Problem Framing

Clarifying prompts to define problems precisely. Reframe the problem when AI responses miss the mark or seem confused.

Refining

Iterating deliberately on prompts and responses. Use iteration to break out of rehash loops and improve output quality.

Critical Thinking

Carefully reviewing and questioning AI-generated code. Break out of the vibe coding loop regularly, read what the AI produced, and question it as if it came from a teammate’s pull request. Without this deliberate pause, small errors can pile up into long-term problems.

Critical thinking is the capstone habit of the framework that keeps you in control of your codebase. Without it, the AI writes code; with it, you retain the understanding you need to keep the AI on track.

These habits work together to transform AI from a shortcut that risks bypassing essential thinking into a collaborative tool that enhances thoughtful development practices. Figure 1 shows how these five habits relate to each other in practice.

Figure 1. In the Sens-AI Framework, the three foundation habits (Context, Research, and Problem Framing) overlap and inform each other, connected by iterative Refining, all encompassed by Critical Thinking as the capstone habit

Context

Context is the foundation of effective AI-assisted development. It defines the information, constraints, and goals you provide to guide the AI’s work. Providing context means giving the AI problem details, requirements, domain knowledge, architecture constraints, and expectations for the code you want to generate.

Providing sufficient context helps you shape the problem space clearly. It’s your chance to highlight what really matters, share insights about the environment or use case, and make sure the AI understands the scope and constraints that define success.

The Context habit means deliberately asking: What does the AI need to know to do this well? The clearer your context, the more likely the AI will produce useful code. But if your context is vague or incomplete, the AI won’t ask for clarification; it will confidently generate generic answers that often miss your real problem.

I teach context using a “map” metaphor in my O’Reilly course on the Sens-AI Framework. LLM-based AIs navigate an enormous cloud of learned patterns. Some researchers describe the model as a “space” of vectors representing possible solutions. Your prompt is the guide to help the AI find the right spot in that space. Strong context acts like a precise map, telling the AI which part of that space contains relevant patterns for your specific problem. You’re helping the AI filter out irrelevant possibilities and guiding it toward useful solutions.

Research

Research is the habit of deliberately investigating the problem space when the AI’s initial response falls short of solving your real problem. Even with solid context, LLM responses are entropic and probabilistic: you can’t be certain your prompt will land in exactly the right region of the model’s information space. Research is how you notice where responses miss the mark, figure out what’s lacking, and supply the missing insight to steer the AI more precisely. It’s the first step in fine-tuning your map to reach the right part of the model’s vector space.

Research becomes especially important when you hit barriers like rehash loops, where the AI generates variations without meaningful progress. We saw earlier that the rehash loop is a signal that the AI ran out of context, which means that it exhausted what it can do with its current understanding. When you get that signal, you need to learn more before re-engaging, by reading documentation, clarifying requirements with teammates, thinking through design implications, or even starting another session to ask research questions from another angle. Starting a new chat with a different AI can be especially valuable as a research tool, because your prompt might steer it toward a different region of its information space and help you add the missing context.

Building a Research habit means recognizing that an incomplete response from the AI is often just a step toward getting the right answer. It helps you to see these moments not as failure, but as a signal for you to do the extra work—often just a small amount of targeted research—that gives the AI the information it needs to get to the right place in its massive information space.

Problem Framing

Problem Framing is the habit of transforming your understanding into a clear, precise question that the AI can answer. This is where you apply the insights you gained through Research to shape the prompt in a way that directs the AI toward a genuinely useful solution. AI doesn’t think for you; it can’t generate new insight on its own. It recombines patterns it has seen before in training, and while this can seem insightful, it’s really your context that helps it connect those patterns in useful ways. Your framing is what gives it the clues it needs to assemble those pieces to address your specific problem.

Effective Problem Framing involves rewording questions, adding missing context, explicitly stating constraints, or breaking complex problems into clearer sub-problems. For example, your initial prompt of “make this code better” might return generic suggestions. After brief research on design principles like SOLID and DRY, you could refine it to “this class is too long and mixes authentication with validation, help me separate those concerns.” Or if “how do I handle errors?” led to a rehash loop, research on error handling patterns might update the prompt to “how can I handle retries and rollbacks to maintain consistency?” In both cases, the research provided just enough additional context to transform the prompt.

Problem Framing builds directly on the work you did in Research. It turns the information you gathered into an actionable, targeted question that can steer the AI away from default, safe responses and toward something genuinely useful. This habit is what makes AI prompting an act of thoughtful design, not just trial and error.

Refining

Refining is the habit of iterating deliberately on your prompts and the AI’s responses to move closer to a genuinely useful solution. This habit builds directly on Research and Problem Framing by ensuring your carefully designed prompt actually delivers the result you need. Even with strong context, research, and framing, your first AI output is rarely perfect. Refining means treating the process as a conversation where you evaluate what the AI produced, identify what’s missing or off-target, and adjust your prompt to steer it more effectively. It turns prompting from a single-shot request into an iterative, collaborative design process where your critical thinking remains in the loop.

Refining is what ties the other habits together through this cycle of evaluation and improvement. When the AI’s response misses the mark, it helps you consider: Is the context insufficient? Do you need more research? Should you reframe the problem? Refining helps you decide whether to tweak your current prompt or step back to an earlier habit. For example, if the AI generates code that compiles but doesn’t handle edge cases you mentioned, Refining might mean adding those requirements more explicitly to the prompt or stepping back to research how similar systems handle those scenarios before trying again.

More broadly, Refining is the habit that breaks you out of vibe coding that’s turned unproductive by using the AI’s responses as signals to guide your next move. And Refining doesn’t necessarily mean adding a huge amount of detail to your prompt.

I follow the pattern of vibe coding and then Refining frequently in my own development: working quickly with AI until I get a signal that something’s off, then pausing to closely read what it generated and making a targeted adjustment to get back on track. Most of the time, I just need to add a few clarifying words or specify a constraint the AI missed to get the AI back on track before returning to the vibe coding loop.

Critical Thinking

Critical Thinking is the practice of carefully reviewing and questioning the code AI generates before you accept or ship it, and it’s the capstone habit of this framework. This habit is especially important because AI is so good at producing code that looks right. It might compile, pass a simple test, or even follow familiar patterns. But under that surface-level polish, there can be subtle bugs, poor design decisions, security risks, or maintainability issues. Critical Thinking is about slowing down enough to review, question, and verify: Does this code really solve the problem? Does it handle edge cases? Is it secure, maintainable, and aligned with our design goals?

When I added AI exercises to Head First C#, one of the biggest challenges was making sure learners didn’t treat the AI’s output as “the answer” without questioning it. They needed to learn to review, critique, and really think about what the code was doing. This habit is just as important for professional developers, especially when working with AI in fast, fluid vibe coding sessions. The vibe coding loop works because it keeps you moving quickly; its downside is that continuously accepting outputs without reviewing or questioning them can switch you out of critical thinking mode entirely.

AI generates a lot of code, and it’s simply not practical to review every line in detail every time it regenerates. In fact, trying to read all of it can lead to cognitive overload, or the mental exhaustion from trying to understand too much code at once, and makes you hesitant to discard code that isn’t working simply because you already spent time reading it. A better approach is to use your critical thinking early in the loop: if you spot even a small design issue or readability problem, it’s fine to stop and ask the AI to regenerate or refactor before you commit to reading the entire thing. If it sounds like I’m giving you permission not to read all of the AI-generated code, good. I am.

Readability and comprehension are critical thinking tools in themselves. By shaping prompts that improve code clarity and make the generated code more readable, you make future reviews easier and avoid filling your head with code you’ll eventually throw out anyway. When you do break out of the vibe coding loop to read the generated code, you can avoid cognitive overload by focusing on the parts where design decisions hide and technical debt grows, like boundaries between components, contracts and responsibilities, and assumptions baked into interfaces and surface design.

Unit tests are a practical tool for enforcing critical thinking. If you ask the AI to generate a class, also have it generate unit tests. When adding or updating a unit test feels unreasonably difficult, that’s often the first sign the design is too rigid. The test is telling you something about the structure: for example, maybe the code is too intertwined, or the boundaries are unclear. This feedback loop works whether the code was AI-generated or handwritten, but with AI the friction often shows up later, after the code has already been merged.

If writing tests for the AI’s code proves difficult, requiring excessive mocking, too many dependencies, or modifications to unrelated parts, that’s a clear signal the design is too coupled or unclear. That’s your cue to stop, read the code carefully, and rethink the design.

But you don’t always have to do all that reading yourself. Another practical technique is to let the AI help you by generating unit tests that validate the behavior you want, review those tests, and then have the AI lock in that behavior. This forces you to specifically describe the behavior you expect, often in simple terms, which gives the AI additional context to help it determine whether the code behaves correctly. It’s especially helpful to ask for the AI to focus on readability by specifically instructing it to generate tests that are easy to read and review. Tests are easier to read than implementation details, and the behavior they’re checking tends to stay stable even if you regenerate the underlying code. This idea is drawn from test-driven development: the tests themselves help the AI form a better understanding of the behavior you’re looking for and encourage better class design and architecture. Using AI this way reduces cognitive load while still enforcing critical thinking about design.

Ultimately, Critical Thinking is the habit that keeps you in control of your work. AI can be an incredibly useful tool, but it can’t understand your goals, constraints, or quality standards the way you do. This habit ensures that you bring your judgment, including design sense, domain expertise, and testing instincts, to every piece of code you accept. It’s the capstone habit of the Sens-AI Framework because it ties everything else together by making sure you stay engaged, reflective, and responsible for the software you build.

Bringing the Habits Together

The Sens-AI Framework is designed to help developers use AI tools thoughtfully and effectively without sacrificing the critical thinking, design sense, and judgment that make software maintainable and robust. These five habits—Context, Research, Problem Framing, Refining, and Critical Thinking—aren’t a strict process to follow in order, but flexible tools you can apply as needed to stay in control of your work.

Let’s look at some common issues that arise during AI-assisted coding sessions, and how you can use the five habits of the Sens-AI Framework to address them. These issues are familiar to developers because they often appear in real development work. We can treat them as signals that tell us when to shift our approach, step back to research, or apply more critical thinking to what the AI is producing. Recognizing those signals, and knowing which habit to apply in response, can mean the difference between productive AI collaboration and frustrating dead ends. Table 1 lists examples of common signals during AI-assisted coding sessions, their likely root causes, and ways to use Sens-AI habits to coach developers toward more thoughtful use.

Table 1. Signals, root causes, and coaching with Sens-AI habits

Signal you see | Root cause | How to address with Sens-AI
AI keeps repeating partial variations (rehash loop) | Poorly defined or ambiguous problem because the AI ran out of context | Coach the developer to pause and do research to provide missing context in the prompt
Generated code looks right but doesn’t quite work | AI’s insufficient understanding of the domain or problem requirements | Guide the developer to do targeted research before prompting again
Code that almost works but has subtle bugs | AI-filled gaps with plausible but incorrect assumptions | Verify assumptions and test edge cases thoroughly
Small changes require cascading edits (shotgun surgery) | Growing web of interdependencies and poor separation of concerns | Review architecture and refactor for cleaner boundaries
Unit tests require extensive changes to previously generated code or excessive mocking | Poor separation of concerns or unclear boundaries in design | Review architecture and interfaces with the AI to help it refactor the generated code
Fragile unit tests that are overly complex | Design is too tightly coupled or responsibilities are unclear | Step back and rethink the design before writing more tests
Small fixes require extensive fixes in other parts of the codebase (shotgun surgery) | Underlying design flaws and hidden dependencies | Map out dependencies, refactor to reduce coupling, and generate unit tests to help keep code decoupled
Developer always skips code review or testing steps | Overconfidence in AI output | Coach the developer to deliberately refine and question AI output
Developer gets bogged down trying to read every line of AI-generated code | Cognitive overload from trying to review everything the AI produces | Encourage the developer to stop reading deeply, return to vibe coding, and use unit tests to validate behavior
Developer keeps using the same research approach when stuck and is unable to break out of rehash loops | Limited exploration of available information sources | Suggest trying different AIs, new chat sessions, or other documentation sources

Lessons from Teaching and Real-World Adoption

The Sens-AI Framework didn’t come out of theory or abstraction. I developed it directly from the messy, real work of trying to teach developers how to use AI tools without giving up the critical thinking that makes them effective engineers. These habits emerged from watching developers hit real obstacles, often in frustrating ways, and were refined through practical use across revisions of Head First C#, training sessions, and coaching with professional developers. Validation didn’t necessarily come from formal metrics, but from watching people recognize frustration signals, adopt the habits deliberately, and produce better, more maintainable code.

This example from a Reddit post written by a new developer shows exactly why I designed the Sens-AI Framework. It demonstrates how accessible these tools are to complete beginners. This new developer was able to write enough code to feel successful for several months, but then ran into a wall:

I just want to say that I am giving up on creating anything anymore. I was trying to create my little project, but every time there are more and more errors and I am sick of it. I am working on it for about 3 months, I do not have any experience with coding and was doing everything through AI (Cursor, ChatGPT etc.)... So I do not have any more energy in me to work on this. It is hopeless. AI is still just soooooo stupid and it will fix one thing but destroy 10 other things in your code (SpacetimeSorcerer 2025).

The post describes a problem familiar to many experienced software engineers. The design had hardened around early mistakes, and every change brought a wave of debugging. That caused the developer to hit the wall known in software design as shotgun surgery, where a single change ripples through so much code that it’s risky and slow to work on (Fowler 2018). Many developers are familiar with this frustration: one small change ends up requiring another change to a different part of the code, then another, cascading into long chains of edits scattered across the codebase.

Shotgun surgery is a classic sign of technical debt; it’s the hidden cost of early shortcuts that make future changes harder and more expensive. In the past, new and early-career developers rarely experienced it, because they did not yet have the technical skills to create a large, layered codebase.

This example also shows how someone new to coding may not even realize that technical debt is starting to pile up until it’s too late, because they haven’t yet built the tools or habits to recognize it early. Normally the shotgun surgery pattern emerges in codebases that have accumulated technical debt over time. But here, someone with no prior coding experience has hit the same wall that experienced teams face, because AI helped them generate tightly coupled code so quickly that architectural problems compounded, and the developer didn’t have the coding experience to recognize the warning signs and fix the problems early. This is a real-world demonstration of the cognitive shortcut paradox: the tool that made coding accessible helped create the problems that require exactly the experience the developer hasn’t had a chance to build.

Experienced developers routinely find themselves accumulating technical debt too, especially when senior managers set unrealistic deadlines that prioritize delivery over maintainability. The difference is that an experienced developer often knows they’re taking on debt. They can spot antipatterns early because they’ve seen them repeatedly, and then take steps to “pay off” the debt before it gets much more expensive to fix.

But AI is changing the game for experienced developers in ways that go beyond technical debt management, and it’s starting to reshape their traditional career paths.

This shift in how AI changes career dynamics became clear to me through observing developers over the years. A few decades ago, I worked with a developer who was widely respected by everyone on our team. Much of that respect came from the fact that he kept adopting new technologies that none of us had worked with. There was a cutting-edge language at the time that few people were using, and he built an entire feature with it. He quickly became known as the person you’d go to for these niche technologies, and it earned him a lot of respect from the rest of the team.

Years later, I worked with another developer who went out of his way to incorporate specific, obscure .NET libraries into his code. That too got him recognition from our team members and managers, and he was viewed as a senior developer in part because of his expertise with these specialized tools.

In the past, these experienced developers could build deep expertise in a single technology (like Rails or React) and that expertise would consistently get them recognition on their team and help them stand out in reviews and job interviews. It used to take months or years of working with a specific framework before a developer could write idiomatic code, or code that follows the accepted patterns and best practices of that technology. But now AI models are trained on countless examples of idiomatic code, so developers without that experience can generate similar code immediately. That puts less of a premium on the time spent developing that deep expertise.

That change is reshaping career paths in ways we’re just starting to see. The traditional approach worked for decades, but as AI fills in more of that specialized knowledge, the career advantage is shifting toward people who can integrate across systems and spot design problems early.

As I’ve trained developers and teams who are increasingly adopting AI coding tools, I’ve noticed that the developers who adapt best aren’t always the ones with the deepest expertise in a specific framework. Rather, they’re the ones who can spot when something looks wrong, integrate across different systems, and recognize patterns. Most importantly, they can apply those skills even when they’re not deep experts in the particular technology they’re working with.

That’s additional evidence to support a shift from the more traditional dynamic on teams, where being an expert in a specific technology (like being the “Rails person” or the “React expert” on the team) carried real authority. AI now fills in much of that specialized knowledge. You can still build a career on deep Rails knowledge, but it doesn’t necessarily carry the same authority on a team that it once did.

What AI still can’t do is tell you when a design or architecture decision today will cause problems six months from now, or when you’re writing code that doesn’t actually solve the user’s problem. That’s why being a generalist—with skills in architecture, design patterns, requirements analysis, and even project management—is becoming more valuable on software teams. And it makes the cognitive shortcut paradox even more dangerous for early-career developers, because some of those broader skills, especially the more technical ones, typically require years of coding to build.

Like the Reddit user who ran into shotgun surgery problems in their AI-generated code, new developers are especially vulnerable to all of these problems because of the cognitive shortcut paradox. Without enough hands-on experience debugging, refactoring, and working through ambiguous requirements, they don’t have the instincts built up through experience to spot structural problems in AI-generated code. The AI can hand them a seemingly clean, working solution. But if they can’t see the design flaws hiding inside it, those flaws grow unchecked until they’re locked into the foundations of the project. By the time a less experienced developer finally sees the scope of the problem, fixing it requires extensive, frustrating work.

To remedy problems like technical debt, shotgun surgery, and the cognitive shortcut paradox, the habit-based approach of the Sens-AI Framework is designed to help you keep your judgment, design sense, and critical thinking firmly in the loop when using AI tools. It was developed on real projects, rooted in the experience of watching myself and others get stuck, change our approach, and find something that worked.

Understanding Developer Challenges with AI

Using AI tools feels deceptively easy at first: you can just describe what you want, and the AI generates working code. But that same simplicity can be misleading. I’ve seen developers, including myself, fall into predictable traps: accepting code that looks right without stopping to verify if it actually solves the problem, or getting stuck when the AI keeps generating similar but unhelpful variations.

These experiences also reveal something important about how developers interact with AI. There’s an emotional component: frustration when things don’t work, overconfidence when they seem to, and a tendency to blame either yourself or the AI when you get stuck. In my training and coaching sessions, I’ve seen even experienced developers treat AI suggestions as authoritative, skipping the review and testing they’d normally do with code from a colleague.

Much of my approach grew out of my own coding experience. I’d been hitting rehash loops for a long time without recognizing them as a signal that the AI had run out of context. One project that drove this home was a COBOL terminal app. I was curious whether this decades-old mainframe language could handle interactive games. I hadn’t used COBOL before, so I was using ChatGPT to help build a simple COBOL application as a learning exercise. The early outputs looked promising: my COBOL code could clear the screen, position the cursor, and handle basic input. But I hit a subtle bug: ANSI escape codes were printing literally to the console instead of moving the cursor. I kept tweaking the prompt, getting variations that looked plausible but didn’t work. At the time, I didn’t see the rehash loop for what it was, so I just gave up, assuming ChatGPT couldn’t give me a good answer, and moved on to a new project. But after developing the Sens-AI Framework, I revisited it and treated it as a test case. Deliberately applying the five habits worked extremely well: adding missing context, briefly using another AI to research COBOL string formatting, reframing the problem, iterating on the prompt, and actually reading through the code that it produced. The problem that had wasted hours when I first tried to solve it with AI was resolved in minutes. That experience convinced me just how powerful these habits could be in practice.

My COBOL experience taught me to recognize rehash loops in my own work. But this pattern isn’t limited to solo projects or unfamiliar languages. Another powerful example came from my friend and colleague Luis, a skilled developer and an advanced AI user.

Luis was building a desktop wrapper for his React app using Tauri, which is Rust-based. He hadn’t used Rust before, and was relying on Copilot and ChatGPT to scaffold the project. Both tools produced plausible-looking Rust code, but he ran into persistent state management bugs. As Luis described his experience:

When setup succeeded, injection failed and the state would be empty. So it would tell me I need to change it to NOT be an Arc wrapped entity, and when I changed that, injection worked and the setup failed. Round and round we went. It could not keep enough context to know how to solve all three cases. And it is important to note I used both Copilot and ChatGPT and neither could find the proper solution.

Because it was a personal project, he could afford to set it aside and return to it in small increments. He spent days toggling between Arc wrappers, pointers, cloning, and parameter passing, with the AI offering solutions that fixed part of the problem but broke another. Eventually, he gave up on prompting and spent some time on Stack Overflow. He took the time to understand the problem, and learned just enough to see what information the AI tools were missing so he could guide them better. Once Luis reframed the problem and provided the context the AI was missing, it was able to provide a solution to the problem. Luis’s experience is a textbook case of how to handle a rehash loop. He recognized when the AI kept circling partial solutions, stopped, did some research, and reframed the problem to provide the missing context.

Luis’s experience shows this isn’t just about inexperience with AI tools; even seasoned developers can get caught in these patterns. Both stories illustrate the same fundamental challenge: recognizing when AI has run out of useful context and knowing how to step back and provide what’s missing.

The Sens-AI Framework habits were built specifically to address the kinds of problems these stories show: getting caught in rehash loops, accepting partial solutions without question, missing the chance to step back and understand what’s really needed, and remembering to bring critical thinking back into the process even when the AI seems helpful.

Prompt Problems Aren’t New: Lessons from 50 Years of Software Engineering

In the push to get the most from AI tools, prompt engineering, or the practice of writing clear, structured inputs that guide an AI tool’s output, has taken center stage. But for software engineers, the skill isn’t new. We’ve been doing a version of it for decades, just under a different name. The challenges we face when writing AI prompts are the same ones software teams have been grappling with for generations. Talking about prompt engineering today is really just continuing a much older conversation about how developers spell out what they need built, under what conditions, with what assumptions, and how to communicate that to the team.

The software crisis was the name given to this problem starting in the late 1960s, especially at the NATO Software Engineering Conference in 1968, where the term “software engineering” was introduced. The crisis referred to the widespread industry experience that software projects were over budget, late, and often failed to deliver what users actually needed (NATO 1969).

There was a common misconception that these failures were due to programmers lacking technical skill, or teams who needed more technical training. But the panels at that conference focused on what they saw as the real root cause: teams and their stakeholders had trouble understanding the problems they were solving and what they actually needed to build, communicating those needs and ideas clearly among themselves, and ensuring the delivered system matched that intent. It was fundamentally a human communication problem.

Participants at the conference captured this precisely. Dr. Edward E. David Jr. from Bell Labs noted there is often no way even to specify in a logically tight way what the software is supposed to do. Douglas Ross from MIT pointed out the pitfall where you can specify what you are going to do, and then do it as if that solved the problem. Prof. W.L. van der Poel summed up the challenge of incomplete specifications: most problems simply aren’t defined well enough at the start, so you don’t have the information you need to build the right solution.

These are all problems that cause teams to misunderstand the software they’re creating before any code is written. And they should all sound familiar to developers today who work with AI to generate code.

Much of the problem boils down to what I’ve often called the classic “do what I meant, not what I said” problem. Machines are literal, and people on teams often are too. Our intentions are rarely fully spelled out, and getting everyone aligned on what the software is supposed to do has always required deliberate, often difficult work.

Fred Brooks wrote about this in his classic and widely influential “No Silver Bullet” essay. He argued that while tools can reduce some of the complexity involved in software development, they can’t eliminate the inherent difficulty of understanding what needs to be built and communicating that intent clearly (Brooks 1995). Throughout the history of software engineering, teams have been tempted to look for that silver bullet that would make the hard parts of understanding and communication go away:

Not only are there no silver bullets now in view, the very nature of software makes it unlikely that there will be any—no inventions that will do for software productivity, reliability, and simplicity what electronics, transistors, large-scale integration did for computer hardware.

Frederick P. Brooks, The Mythical Man-Month: Essays on Software Engineering

It shouldn’t be surprising that we’d see the same problems that plagued software teams for years reappear when they started to use AI tools.

By the end of the 1970s, these problems were being reframed in terms of quality. Philip Crosby, Joseph M. Juran, and William E. Deming, three people who had enormous influence on the field of quality engineering, each had influential takes on why so many products didn’t do the jobs they were supposed to do, and these ideas are especially true when it comes to software. Crosby argued quality was fundamentally conformance to requirements: if you couldn’t define what you needed clearly, you couldn’t ensure it would be delivered (Crosby 1979). Juran talked about fitness for use; software needed to solve the user’s real problem in its real context, not just pass some checklists (Juran 1999). Deming pushed even further, emphasizing that defects weren’t just technical mistakes but symptoms of broken systems, and especially poor communication and lack of shared understanding. He focused on the human side of engineering: creating processes that help people learn, communicate, and improve together (Deming 1986).

Through the 1980s, these insights from the quality movement were applied to software development and started to crystallize into a distinct discipline called requirements engineering, focused on identifying, analyzing, documenting, and managing the needs of stakeholders for a product or system. It emerged as its own field, complete with conferences, methodologies, and professional practices. The IEEE Computer Society formalized this with its first International Symposium on Requirements Engineering in 1993, marking its recognition as a core area of software engineering (IEEE Computer Society 1993).

The 1990s became a heyday for requirements work, with organizations investing heavily in formal processes and templates, believing that better documentation formats would ensure better software. Standards like IEEE-830 codified the structure of software requirements specifications, and process models such as the Software Development Life Cycle and CMM/CMMI emphasized rigorous documentation and repeatable practices. Many organizations invested heavily in designing detailed templates and forms, hoping that filling them out correctly would guarantee the right system. In practice, those templates were useful for consistency and compliance, but they didn’t eliminate the hard part: making sure what was in one person’s head matched what was in everyone else’s.

While the 1990s focused on formal documentation, the Agile movement of the 2000s shifted toward a more lightweight, conversational approach. User stories emerged as a deliberate counterpoint to heavyweight specifications. A user story is a short, simple description of functionality told from the user’s perspective, designed to be easy to write and easy to understand, and to be small enough to fit on a sticky note or index card (Figure 2). Instead of trying to capture every detail upfront, user stories serve as placeholders for conversations between developers and stakeholders (Cohn 2004). The practice was deliberately simple, based on the idea that shared understanding comes from dialogue, not documentation, and that requirements evolve through iteration and working software rather than being fixed at the project’s start.

All of this reinforced requirements engineering as a legitimate area of software engineering practice, and a real career path with its own set of skills. Requirements work is about surfacing assumptions, clarifying goals, and ensuring everyone involved has the same understanding of what needs to be built.

Figure 2. This user story, written on an index card, shows an example of the “As a … I want … so that” format used by many Agile teams

Prompt Engineering Is Requirements Engineering

Prompt engineering and requirements engineering are literally the same skill: using clarity, context, and intentionality to communicate your intent and ensure what gets built matches what you actually need.

User stories were an evolution from traditional formal specifications: a simpler, more flexible approach to requirements, but with the same goal of making sure everyone understood the intent. They gained wide acceptance across the industry because they helped teams recognize that requirements are about creating a shared understanding of the project. User stories gave teams a lightweight way to capture intent and then refine it through conversation, iteration, and working software.

Prompt engineering plays the exact same role. The prompt is our lightweight placeholder for a conversation with the AI. We still refine it through iteration, adding context, clarifying intent, and checking the output against what we actually meant. But it’s the full conversation with the AI and its context that matters; the individual prompts are just a means to communicate the intent and context. Just like Agile shifted requirements from static specs to living conversations, prompt engineering shifts our interaction with AI from single-shot commands to an iterative refinement process. But unlike when we work with a team of humans, we typically have to infer what’s missing from the output ourselves, because the AI is much more likely to hallucinate missing requirements than it is to ask clarifying questions.

User stories intentionally focused the engineering work back on people and what’s in their heads. Whether it’s a requirements document in Word or a user story in Jira, the most important thing isn’t the piece of paper, ticket, or document we wrote. The most important thing is that what’s in my head matches what’s in your head and matches what’s in the heads of everyone else involved. The piece of paper is just a convenient way to help us figure out whether or not we agree.

Prompt engineering demands the same outcome. Instead of working with teammates to align mental models, we’re communicating to an AI, but the goal hasn’t changed: producing a high-quality product. The basic principles of quality engineering laid out by Deming, Juran, and Crosby have direct parallels in prompt engineering:

Deming’s focus on systems and communication

Prompting failures can be traced to problems with the process, not the people. They typically stem from poor context and communication, not from “bad AI.”

Juran’s focus on fitness for use

When he framed quality as “fitness for use,” he meant that what we produce has to meet real needs, not just look plausible. A prompt is useless if the output doesn’t solve the real problem, and failure to create a prompt that’s fit for use is likely to result in hallucinations.

Crosby’s focus on conformance to requirements

Prompts must specify not just functional needs, but also nonfunctional ones like maintainability and readability. If the context and framing aren’t clear, the AI will generate output that conforms to its training distribution rather than the real intent.

One of the clearest ways these quality principles show up in prompt engineering is through what’s now called context engineering: deciding what the model needs to see to generate something useful, which typically includes surrounding code, test inputs, expected outputs, design constraints, and other important project information. If you give the AI too little context, it fills in the blanks with what seems most likely based on its training data (which usually isn’t what you had in mind). If you give it too much, it can get buried in information and lose track of what you’re really asking for. That judgment call of what to include and what to leave out has always been one of the deepest challenges at the heart of requirements work.

There’s another important parallel between requirements engineering and prompt engineering. Back in the 1990s, many organizations fell into what we might call the template trap: believing that the right standardized form or requirements template could guarantee a good outcome. Teams spent huge effort designing and filling out documents. But the real problem was never the format; it was whether the underlying intent was truly shared and understood.

Today, many companies fall into a similar trap with prompt libraries, or catalogs of pre-written prompts meant to standardize practice and remove the difficulty of writing prompts. Prompt libraries can be useful as references or starting points, but they don’t replace the core skill of framing the problem and ensuring shared understanding. Just like a perfect requirements template in the 1990s didn’t guarantee the right system, canned prompts today don’t guarantee the right code.

Decades later, the points Brooks made in his “No Silver Bullet” essay still hold. There’s no single template, library, or tool that can eliminate the essential understanding what needs to be built. Whether it’s requirements engineering in the 1990s or prompt engineering today, the hard part is always the same: building and maintaining a shared understanding of intent. Tools can help, but they don’t replace the discipline. And that discipline comes back to critical thinking, the habit of stepping out of the loop, questioning assumptions, and making sure the code actually does what you need it to do.

AI raises the stakes on this core communication problem. Unlike your teammates, the AI won’t push back or ask questions. It just generates something that looks plausible based on the prompt that it was given. That makes clear communication of requirements even more important.

The alignment of understanding that serves as the foundation of requirements engineering is even more important when we bring AI tools into the project, because AI doesn’t have judgment. It has a huge model, but it only works effectively when directed well. The AI needs the context that we provide in the form of code, documents, and other project information and artifacts, which means the only thing it knows about the project is what we tell it. That’s why it’s especially important to have ways to check and verify that what the AI “knows” really matches what we know.

The classic requirements engineering problems are compounded when we use AI. That’s especially true of the poor communication and lack of shared understanding that Deming warned about, and that requirements engineers and Agile practitioners have spent decades trying to address. We’re still facing the same issues of communicating intent and specifying requirements clearly. But now those requirements aren’t just for the team to read; they’re used to establish the AI’s context. Small variations in problem framing can have a profound impact on what the AI produces. Using natural language to increasingly replace the structured, unambiguous syntax of code removes a critical guardrail that’s traditionally helped protect software from failed understanding: when you write code, the compiler or interpreter catches ambiguities and forces you to be precise. With natural language prompts, there’s no such check; the AI just generates something plausible based on what you said, whether you said it clearly or not.

The tools of requirements engineering help us make up for that missing guardrail. Agile’s iterative process of the developer understanding requirements, building working software, and continuously reviewing it with the product owner are especially helpful, because they add a check that ensures misunderstandings are caught early (Stellman and Greene 2014). The more we eliminate that extra step of translation and understanding by having AI generate code directly from requirements, the more important it becomes for everyone involved, stakeholders and engineers alike, to have a truly shared understanding of what needs to be built.

When people on teams work together to build software, they spend a lot of time talking and asking questions to understand what they need to build. Working with an AI follows a similar but distinct kind of feedback cycle. You don’t know the AI is missing context until you see what it produces, and you often need to reverse engineer what it did to figure out what’s missing. But both types of interaction require the same fundamental skills around context and communication that requirements engineers have always practiced.

Adapting traditional requirements engineering skills to prompt engineering shows up in practice in several ways:

Context and shared understanding are foundational

Good requirements help teams understand what behavior matters and how to know when it’s working, which involves capturing both functional requirements (what to build) and non-functional requirements (how well it should work). The same distinction applies to prompting, but with fewer chances to course-correct. If you leave out something critical, the AI doesn’t push back; it just responds with whatever seems plausible. Sometimes that output looks reasonable until you try to use it and realize the AI was solving a different problem.

Scoping takes real judgment

Developers who struggle to use AI for code routinely fall into two extremes: providing too little context (a single sentence that produces something that looks right but fails in practice) or pasting in entire files expecting the model to zoom in on the right method. Unless you explicitly call out what’s important by specifying both functional and non-functional requirements, you probably haven’t provided the AI with enough context to generate the code that fully solves your problem.

Context drifts, and the model doesn’t know it’s drifted

With human teams, understanding shifts gradually through check-ins and conversations; without that interaction, the team’s understanding of the problem can slowly drift away from what they’re supposed to be building. With prompting, drift can happen in just a few exchanges. The model might still be generating fluent responses until it suggests a fix that makes no sense. That’s a signal that the context has drifted, and you need to reframe the conversation, perhaps by asking the model to explain the code or restate what it thinks it’s doing.

History keeps repeating itself: from binders full of scattered requirements to IEEE standards to user stories to today’s prompts, the discipline is the same. We succeed when we treat it as real engineering. Prompt engineering is the next step in the evolution of requirements engineering. It’s how we make sure we have a shared understanding between everyone on the project—including the AI—and it demands the same care, clarity, and deliberate communication we’ve always needed to avoid misunderstandings and build the right thing.

Trust but Verify: Learning to Catch What AI Misses

We often say AIs “understand” code, but they don’t truly understand your problem or your codebase in the sense that humans understand things. They’re mimicking patterns from text and code they’ve seen before, either built into their model or provided by you, aiming to produce something that looks right and is a plausible answer. It’s very often correct, which is why vibe coding works so well, but it’s not guaranteed to be correct. And because of the limitations of how LLMs work and how we prompt with them, the solutions rarely account for overall architecture, long-term strategy, or often even good code design principles.

The principle I’ve found most effective for managing these risks is borrowed from another domain entirely: trust but verify. While the phrase has been used in everything from international relations to systems administration, it perfectly captures the relationship we need with AI-generated code. We trust the AI enough to use its output as a starting point, but we verify everything before we commit it.

“Trust but verify” is the cornerstone of an effective approach: trust the AI for a starting point, but verify that the design supports change, testability, and clarity. That means applying the same critical review patterns you’d use for any code: checking assumptions, understanding what the code is really doing, and making sure it fits your design and standards.

Verifying AI-generated code means reading it, running it, and sometimes even debugging through it line by line. It can involve making sure that it’s readable, for example by asking yourself whether the code will still make sense to you (or anyone else) months from now. In practice, this can mean quick design reviews even for AI-generated code, refactoring when coupling or duplication starts to creep in, and taking a deliberate pass at naming so variables and functions read clearly. These extra steps help you stay engaged with critical thinking, and keep you from locking early mistakes into the codebase where they become difficult to fix.

Verifying also means taking specific steps to check both your assumptions and the AI’s output, like generating unit tests for the code, as we discussed earlier. The AI can be helpful, but it isn’t reliable by default. It doesn’t know your problem, your domain, or your team’s context unless you make that explicit in your prompts and review the output carefully to make sure that you communicated it well and the AI understood.

AI can help with this verification too: it can suggest refactorings, point out duplicated logic, or help extract messy code into cleaner abstractions. But it’s up to you to direct it to make those changes, which means you have to spot them first. Experienced developers who have seen similar problems over the course of many projects are well-positioned to spot them in AI-generated code.

Beyond reviewing the code directly, there are several techniques that can help with verification. They’re based on the idea that the AI generates code based on the context it’s working with, but it can’t tell you why it made specific choices the way a human developer could. When code doesn’t work, it’s often because the AI filled in gaps with assumptions based on patterns in its training data that don’t actually match your actual problem. These techniques are designed to help surface those hidden assumptions, highlighting options so you can make the decisions about your code instead of leaving them to the AI:

Ask the AI to explain the code it just generated.

Follow up with questions about why it made specific design choices. The explanation isn’t the same as a human author walking you through their intent; it’s the AI interpreting its own output. But that perspective can still be valuable, like having a second reviewer describe what they see in the code. If the AI made a mistake, its explanation will likely echo that mistake because it’s still working from the same context. But that consistency can actually help surface the assumptions or misunderstandings you might not catch by just reading the code.

Try generating multiple solutions.

Asking the AI to produce two or three alternatives forces it to vary its approach, which often reveals different assumptions or trade-offs. One version may be more concise; another more idiomatic; a third more explicit. Even if none are perfect, putting the options side by side helps you compare patterns and decide what best fits your codebase. Comparing the alternatives is an effective way to keep your critical thinking engaged and stay in control of your codebase.

Use the AI as its own critic.

After the AI generates code, ask it to review that code for problems or improvements. This can be effective because it forces the AI to approach the code as a new task; the context shift is more likely to surface edge cases or design issues the AI didn’t detect the first time. Because of that shift, you might get contradictory or nitpicky feedback. But that feedback can be useful too, as it reveals places where the AI is drawing on conflicting patterns from its training (or, more precisely, where it’s drawing on contradictory patterns from its training). Treat these critiques as prompts for your own judgment, not as fixes to apply blindly. Again, this is a technique that helps keep your critical thinking engaged by highlighting issues you might otherwise skip over when skimming the generated code.

These verification steps might feel like they slow you down, but they’re actually investments in velocity. Catching a design problem after five minutes of review is much faster than debugging it six months later when it’s woven throughout your codebase. The goal is to go beyond simple vibe coding by adding strategic checkpoints where you shift from generation mode to evaluation mode.

The ability of AI to generate a huge amount of code in a very short time is a double-edged sword. That speed is seductive, but if you aren’t careful with it, you can vibe code your way straight into classic antipatterns. In my own coding, I’ve seen the AI take clear steps down this path, creating overly structured solutions that, if I allowed them to go unchecked, would lead directly to complex, highly coupled, and layered designs. I spotted them because I’ve spent decades writing code and working on teams, so I recognized the patterns early and corrected them, just like I’ve done hundreds of times in code reviews with team members. This means slowing down enough to think about design, a critical part of the mindset of “trust but verify” that involves reviewing changes carefully to avoid building layered complexity you can’t unwind later.

There’s also a strong signal in how hard it is to write good unit tests for AI-generated code. I can’t emphasize this enough: If tests are hard for the AI to generate, that’s a signal to stop and think. Adding unit tests to your vibe coding cycle creates a checkpoint: a reason to pause, question the output, and shift back into critical thinking. This technique borrows from test-driven development, using tests not only to catch bugs later but also to reveal when a design is too complex or unclear (Beck 2004).

When you ask the AI to help write unit tests for generated code, first have it generate a plan for the tests it’s going to write. Watch for signs of trouble: lots of mocking, complex setup, too many dependencies, and especially needing to modify other parts of the code. Those are signals that the design is too coupled or unclear. When you see those signs, stop vibe coding and read the code. Ask the AI to explain it. Run it in the debugger. Stay in critical thinking mode until you’re satisfied with the design.

Table 1 shows many clear signals that these risks are creeping in; signals that tell you when to stop trusting and start verifying. All of these are signals to step out of the vibe coding loop, apply critical thinking, and use the AI deliberately to refactor your code for simplicity.

The AI Teaching Toolkit: Practical Guidance for Teams

Teaching developers to work effectively with AI means helping them build habits that keep critical thinking active while leveraging AI’s speed.

But teaching these habits isn’t straightforward. Instructors and team leads often find themselves needing to guide developers through challenges in ways that build their self-confidence rather than short-circuit their growth. There are also the regular challenges of working with AI:

Suggestions that look correct while hiding subtle flaws

Less experienced developers accepting output without questioning it

AI producing patterns that don’t match the team’s standards

Code that works but creates long-term maintainability headaches

The Sens-AI Framework was built to address these problems.

The toolkit in this section builds on and reinforces those habits by giving you concrete ways to integrate them into team practices, whether you’re running a workshop, leading code reviews, or mentoring individual developers. The techniques that follow include practical teaching strategies, common pitfalls to avoid, reflective questions to deepen learning, and positive signs that show the habits are sticking.

Advice for Instructors and Team Leads

The strategies discussed here can be used in classrooms, review meetings, design discussions, or one-on-one mentoring. They’re meant to help new learners, experienced developers, and teams have more open conversations about design decisions, context, and the quality of AI suggestions. The focus is on making review and questioning feel like a normal, expected part of everyday development.

Discuss assumptions and context explicitly

In code reviews or mentoring sessions, ask developers to talk about occurrences when the AI gave them poor or unexpected results. Also try asking them to explain what they think the AI might have needed to know to produce a better answer, and where it might have filled in gaps incorrectly. Getting developers to articulate those assumptions helps spot weak points in design before they’re cemented into the code.

Encourage pairing or small-group prompt reviews

Make AI-assisted development collaborative, not siloed. Have developers on a team or students in a class share their prompts with each other, and talk through why they wrote them a certain way, just like they’d talk through design decisions in pair or mob programming. This helps less experienced developers see how others approach framing and refining prompts.

Encourage researching idiomatic use of code

One thing that often holds back intermediate developers is not knowing the idioms of a specific framework or language. AI can help with that research. When developers ask for the idiomatic way to do something, they see not just the syntax but also the patterns experienced developers rely on. That shortcut can speed up their understanding and make them more confident when working with new technologies.

Here are two real-world examples—from my own projects—of how using AI to research idioms can help developers quickly adapt:

A developer with deep experience writing microservices but little exposure to Spring Boot can use AI to see the idiomatic way to annotate a class with @RestController and @RequestMapping. They might also learn that Spring Boot favors constructor injection over field injection with @Autowired, or that @GetMapping("/users") is preferred over @RequestMapping(method = RequestMethod.GET, value = "/users").

A Java developer new to Scala might reach for null instead of Scala’s Option types, missing a core part of the language’s design. Asking the AI for the idiomatic approach surfaces not just the syntax but the philosophy behind it, guiding developers toward safer and more natural patterns.

Help developers recognize rehash loops as meaningful signals

When the AI keeps circling the same broken idea, even developers who have experienced this many times may not realize they’re caught in a rehash loop. Teach them to recognize the loop as a signal that the AI has exhausted its context, and that it’s time to step back. That pause can lead to research, reframing the problem, or providing new information. For example, you might stop and say: “Notice how it’s circling the same idea? That’s our signal to break out.” Then demonstrate how to reset: open a new session, consult documentation, or try a narrower prompt.

Research beyond AI

Help developers learn that when they hit walls, they don’t need to just tweak prompts endlessly. Model the habit of branching out: check official documentation, search websites like Stack Overflow, or review similar patterns in your existing codebase. AI should be one tool among many. Showing developers how to diversify their research keeps them from looping and builds stronger problem-solving instincts.

Use failed projects as test cases

Bring in previous projects that ran into trouble with AI-generated code and revisit them with Sens-AI habits. Review what went right and wrong, talk about where it might have helped to break out of the vibe coding loop to do additional research, reframe the problem, and apply critical thinking. Work with the team to write down lessons you learned from the discussion. Holding a retrospective exercise like this lowers the stakes, because developers are free to experiment and critique without slowing down current work. It’s also a powerful way to show how reframing, refining, and verifying could have prevented past issues.

Make refactoring part of the exercise

Help developers avoid the habit of deciding the code is finished when it runs and seems to work. Have them work with the AI to clean up variable names, reduce duplication, simplify overly complex logic, apply design patterns, and find other ways to prevent technical debt. By making evaluation and improvement explicit, you can help developers build the muscle memory that prevents passive acceptance of AI output.

Common Pitfalls to Address with Teams

Even with good intentions, teams often fall into predictable traps. Watch for these patterns and address them explicitly, because otherwise they can slow progress and mask real learning.

The completionist trap

Trying to read every line of AI output even when you’re about to regenerate it. Teach developers it’s okay to skim, spot problems, and regenerate early. Otherwise, they waste time carefully reviewing code they’ll never use, and they risk cognitive overload. The key is to balance thoroughness with pragmatism: know when detail matters, and when speed matters more.

The perfection loop

Endless tweaking of prompts for marginal improvements. Set a limit: after three or four attempts, it’s time to step back and rethink. Developers need to learn that diminishing returns are a sign to change strategy, not to keep grinding. Otherwise, energy that should go toward solving the problem gets lost in chasing minor refinements.

Context dumping

Pasting entire codebases into prompts. Teach scoping by encouraging developers to figure out the minimum context needed for this specific problem. This is especially problematic with limited context windows, where the AI literally can’t see all the code you’ve pasted, leading to incomplete or contradictory suggestions. Teaching developers to be intentional about scope prevents confusion and makes AI output more reliable.

Skipping the fundamentals

Using AI for extensive code generation before understanding basic software development concepts and patterns. Ensure learners can solve simple development problems on their own (without the help of AI) before accelerating with AI on more complex ones. Otherwise, developers risk building shallow knowledge that collapses under pressure. Fundamentals help new developers avoid the cognitive shortcut paradox by teaching them to evaluate AI’s output critically rather than blindly trusting it.

A Team Exercise That Works

Have your team do AI archaeology by taking a piece of AI-generated code from last week and analyzing it together. What assumptions did it make? What patterns did it use? Are they the right ones for your codebase? You might discover the AI consistently uses older patterns your team has moved away from, or that it defaults to verbose solutions when simpler ones exist. These discoveries become teaching moments about your team’s standards and help calibrate everyone’s “code smell” detection for AI output. This retrospective approach is less threatening than real-time critique and strengthens everyone’s judgment.

Signs of Success

Balancing pitfalls with positive indicators helps teams see what good AI practice looks like. When these habits take hold, you’ll notice developers:

Reviewing AI code with the same rigor as human-written code—but only when appropriate

When developers stop saying “the AI wrote it, so it must be fine” and start giving AI code the same scrutiny they’d give a teammate’s pull request, you know the habits are sticking. But they review the code only when they’ve decided that they’re done iterating and vibe coding, and ready to integrate it into the codebase.

Exploring multiple approaches instead of accepting the first answer

Teams that are getting it right don’t settle for the initial response. They ask the AI to generate alternatives, compare them, and use that exploration to deepen their understanding of the problem.

Recognizing rehash loops without frustration

Instead of endlessly tweaking prompts, they treat rehash loops as signals to pause and rethink. This shows they’re learning to manage AI’s limitations rather than fight against them.

Sharing “AI gotchas” with teammates

Developers start saying things like, “I noticed Copilot always tries this approach, but here’s why it doesn’t work in our codebase.” These small observations become collective knowledge that strengthens the whole team.

Asking “Why did the AI choose this pattern?” instead of just asking “Does it work?”

This subtle shift shows developers are moving beyond surface correctness to reasoning about design. It’s a clear sign that critical thinking is active.

Bringing fundamentals into AI conversations

Developers who are working positively with AI tools tend to relate AI output back to core principles, like readability, separation of concerns, or testability. This shows they’re not letting AI bypass their grounding in software engineering.

Treating AI failures as learning opportunities

When something goes wrong, instead of blaming the AI or themselves, developers dig into why. Was it context? Framing? A fundamental limitation? This investigative mindset turns problems into teachable moments.

Reflective Questions for Teams

These questions are meant to help developers pay closer attention to their own habits when they’re working with AI. They can slow the process just enough to invite discussion or personal reflection. You might use them in training, pairing sessions, or even during code review to prompt developers to explain what they’re doing. The goal is to encourage more thoughtful use of AI by making people surface their assumptions and clarify their intent. It’s about keeping the design conversation going, even when the AI seems to offer quick answers:

“What does the AI need to know to do this well?” (Ask this before writing any prompt.)

“What context or requirements might be missing here?” (Helps catch gaps early.)

“Do you need to pause here and do some research?” (Promotes branching out beyond AI.)

“How might you reframe this problem more clearly for the AI?” (Encourages clarity in prompts.)

“What assumptions are you making about this AI output?” (Surfaces hidden design risks.)

“If you’re getting frustrated, is that a signal to step back and rethink?” (Normalizes stepping away.)

“Would it help to switch from reading code to writing tests to check behavior?” (Shifts the lens to validation.)

“Do these unit tests reveal any design issues or hidden dependencies?” (Connects testing with design insight.)

“Have you tried starting a new chat session or using a different AI tool for this research?” (Models flexibility with tools.)

The goal of this toolkit is to help developers build the kind of judgment that keeps them confident with AI while still growing their core skills. When teams learn to pause, review, and refactor AI-generated code, they move quickly without losing sight of design clarity or long-term maintainability. These teaching strategies give developers the habits to stay in control of the process, learn more deeply from the work, and treat AI as a true collaborator in building better software. As AI tools evolve, these fundamental habits—questioning, verifying, and maintaining design judgment—will remain the difference between teams that use AI well and those that get used by it.

Conclusion

The Sens-AI Framework emerged from a simple observation: developers at all levels were hitting the same walls with AI tools, but those who succeeded had developed specific habits for working through problems. These five habits—Context, Research, Problem Framing, Refining, and Critical Thinking—are practices that keep developers in control of their work, understanding not just what the code does but why it’s structured that way.

AI skills aren’t exclusive to senior developers. I’ve seen relatively new developers develop their AI skills quickly because they’ve built these habits quickly. This framework helps new developers use AI without bypassing essential learning. For all developers, it maintains the design judgment and code quality that distinguish professional software engineering from mere code generation.

As AI increasingly becomes standard in development workflows, these habits need to be woven into how we teach, how we collaborate, and how we build tools. Educational programs must teach AI literacy alongside coding skills, helping students recognize failure modes like rehash loops while building genuine problem-solving abilities. Development teams need evolved practices: code reviews that scrutinize AI assumptions, pairing strategies that catch drift from standards, and shared vocabulary for discussing AI-specific challenges like “I’m stuck in a rehash loop” or “The AI keeps defaulting to the old pattern.”

The tools themselves must evolve too. Imagine an IDE that warns: “Possible rehash loop detected: you’ve been iterating on this same approach for 15 minutes,” or one that surfaces the assumptions the AI is making about your architecture. But tools alone won’t fix this. A team’s measure of success for code shouldn’t revolve around AI. Teams still need to understand that code, keep it maintainable, and grow their own skills along the way.

More convincing output requires more sophisticated evaluation. Models will continue improving: they’ll get faster, handle more context, reason better. As that happens, the need for critical thinking about generated code will only intensify. The next generation of developers will never know coding without AI assistance. Our job is to make sure they build lasting engineering habits alongside these tools so AI strengthens their craft rather than hollowing it out.

The Sens-AI habits work alongside today’s tools and are designed to stay relevant to tomorrow’s tools as well. They give teams a way to talk about both the successes and failures they see when using AI. What happens next depends on how educators, tool builders, and teams put those habits into practice, building a future where these powerful tools strengthen the craft of software development rather than erode it.

AI isn’t replacing developers. It’s changing what kinds of developer skills matter most.

Bibliography

Beck, Kent. Extreme Programming Explained: Embrace Change. 2nd ed., Addison-Wesley, 2004.

Brooks, Frederick P. The Mythical Man-Month: Essays on Software Engineering. Anniversary ed., Addison-Wesley, 1995.

Cohn, Mike. User Stories Applied: For Agile Software Development. Addison-Wesley, 2004.

Crosby, Philip B. Quality Is Free: The Art of Making Quality Certain. McGraw-Hill, 1979.

Deming, W. Edwards. Out of the Crisis. MIT Press, 1986.

Doan, Nhi Hoai, et al. “Understanding and Controlling Repetition Neurons and Induction Heads in In-Context Learning.” arXiv, 10 July 2025. arXiv.org/abs/2507.07810.

Fowler, Martin. Refactoring: Improving the Design of Existing Code. 2nd ed., Addison-Wesley, 2018.

Humlum, Anders, and Emilie Vestergaard. “Large Language Models, Small Labor Market Effects.” National Bureau of Economic Research, May 2025. NBER Working Paper No. 33777.

IEEE Computer Society. Proceedings of IEEE International Symposium on Requirements Engineering. IEEE Computer Society, 1993.

Juran, Joseph M., editor. Juran’s Quality Handbook. 5th ed., McGraw-Hill, 1999.

Laster, Brent (foreword by Andrew Stellman). Learning GitHub Copilot. O’Reilly Media, 2025.

Milios, Aristides, et al. “In-Context Learning for Text Classification with Many Labels.” arXiv, 19 Sept. 2023. arXiv.org/abs/2309.10954.

NATO Science Committee. Software Engineering: Report on a Conference Sponsored by the NATO Science Committee, Garmisch, Germany, 1968. NATO, 1969.

Osmani, Addy. Beyond Vibe Coding. O’Reilly Media, 2025.

Phoenix, James, and Mike Taylor. Prompt Engineering for Generative AI. O’Reilly Media, 2024.

SpacetimeSorcerer. “I am giving up.” Reddit, 30 May 2025, https://www.reddit.com/r/SaaS/comments/1lp3scg/i_am_giving_up (retrieved 2025-06-12, with permission).

Stellman, Andrew, and Jennifer Greene. Applied Software Project Management. O’Reilly Media, 2005.

Stellman, Andrew, and Jennifer Greene. Head First C# (5th ed). O’Reilly Media, 2024.

Stellman, Andrew, and Jennifer Greene. Learning Agile. O’Reilly Media, 2014.

Taulli, Tom. AI-Assisted Programming. O’Reilly Media, 2024.

Vizard, Mike. “Survey: AI Tools are Increasing Amount of Bad Code Needing to be Fixed.” DevOps.com, 10 Jan. 2025, https://devops.com/survey-ai-tools-are-increasing-amount-of-bad-code-needing-to-be-fixed.

About the Author

Andrew Stellman is an author, educator, and software developer with over three decades of experience building software and leading teams. He’s written multiple widely read O’Reilly books—including Head First C#, Learning Agile, and Beautiful Teams—which have sold over 150,000 copies and been translated into more than a dozen languages. His work blends deep technical expertise with a practical, team-centered approach to learning and leadership.

As a senior technology leader, Andrew focuses on developer experience, tooling, and AI-assisted workflows at scale. He teaches GitHub Copilot and ChatGPT practices through O’Reilly Live courses, and created the Sens-AI Framework to help early-career developers think critically and grow their skills while working with AI tools.

In addition to his work on AI, Andrew has a long-standing interest in game design and development, software engineering project management, and working with Agile teams—topics he also teaches and writes about.

Andrew lives in Brooklyn, NY, with his wife, a brown cat, and a weird Pomeranian. Outside of work, he produces music, plays piano, bass, and other instruments, and trains in Krav Maga, aikido, and tai chi. He continues to appreciate the Oxford comma.

Learn more or get in touch at stellman-greene.com.