It Ain't Broke: Why Software Fundamentals Matter More Than Ever — Matt Pocock, AI Hero @mattpocockuk
AI coding tools are powerful but can quickly create unmanageable code if used without discipline. Matt Pocock argues that success with AI in software development comes not from delegating everything, but from applying decades-old engineering fundamentals like TDD, Domain-Driven Design, and creating deep, testable modules. The developer's role shifts from a tactical coder to a strategic system designer.
In the age of AI-driven development, fundamental software engineering principles are more critical than ever. The promise of “specs to code”—where developers only write specifications and let AI handle the implementation—often fails in practice. This approach frequently leads to a decline in code quality with each iteration, a phenomenon akin to software entropy, where the system devolves into unmanageable garbage.
The core issue is the misconception that “code is cheap.” In reality, bad code is more expensive than ever because a complex, hard-to-change codebase prevents developers from leveraging the full potential of AI. A good codebase, however, allows AI to be extraordinarily effective. This means software fundamentals are paramount.
From Miscommunication to Shared Understanding
A common failure mode is the AI not building what the developer intended. This stems from a lack of a shared “design concept” between the human and the AI.
- Problem: The developer and the AI lack a mutual understanding of the project’s goals and constraints.
- Solution: Implement a “Grill Me” skill. This prompts the AI to act as an adversary, relentlessly asking questions to explore every branch of the design tree. This process forces a deep dialogue, ensuring both parties reach a shared understanding before any code is written. The resulting conversation can serve as a detailed product requirements document.
The Power of a Ubiquitous Language
Another challenge is when the AI is overly verbose or seems to be “talking across purposes.” This is a language barrier issue.
- Problem: The developer and the AI don’t share a common vocabulary, leading to misinterpretations and inefficient communication.
- Solution: Adopt the Domain-Driven Design (DDD) concept of a “Ubiquitous Language.” Create a central document (e.g., a Markdown file) that defines the core terminology of the project. By training the AI on this glossary, its planning becomes less verbose, its thinking becomes more aligned with the domain, and the resulting implementation is more accurate.
Enforcing Quality with TDD and Feedback Loops
Even when the AI understands the goal, it can produce code that simply doesn’t work. This happens when it moves too quickly without verification.
- Problem: The AI generates large amounts of code at once without checking its work, essentially “outrunning its headlights.” The rate of feedback is your speed limit.
- Solution: Use Test-Driven Development (TDD). TDD forces the AI to take small, deliberate steps: write a failing test, write the code to make it pass, and then refactor. This iterative process, supported by feedback loops like static typing and automated tests, dramatically improves the reliability of the generated code.
Architectural Integrity: Deep vs. Shallow Modules
The effectiveness of TDD is directly tied to the system’s architecture.
- Problem: AI has a tendency to create “shallow modules”—numerous small files with complex interfaces. This structure is difficult for both humans and AI to navigate, understand, and test effectively.
- Solution: Refactor the codebase towards “deep modules,” as described by John Ousterhout in “A Philosophy of Software Design.” Deep modules hide significant functionality behind a simple, well-designed interface. This architecture is inherently more testable, providing clearer boundaries for verification. A codebase with deep modules rewards TDD and provides better feedback to the AI.
The Developer’s New Role: Strategic System Designer
The cognitive load of managing AI-generated code can lead to burnout. A well-structured codebase mitigates this.
- Principle: “Design the interface, delegate the implementation.”
- Application: By focusing on creating robust, well-tested interfaces for deep modules, developers can confidently delegate the internal implementation details to the AI. This allows you to treat modules as “gray boxes,” understanding their purpose and contract without needing to scrutinize every line of internal code.
Your role shifts from a tactical programmer to a strategic designer. You are responsible for the high-level architecture and investing in the design of the system every day. The AI serves as a powerful tactical implementer, but it requires your strategic guidance to build high-quality, maintainable software.