Timeless engineering principles for AI-built software.
AI tools can generate code at machine speed, but human engineers must hold the frame. These mental models provide compressed judgment to evaluate architecture, cut premature complexity, and prevent silent outages.
First Principles Thinking
Stop copying patterns you don't understand. Reason from the ground up — and use AI as a thinking partner, not a vending machine.
Reasoning from Irreducible Truths
First principles thinking is the practice of actively questioning every assumption you take for granted, breaking down a complex problem into its basic components, and building a solution from scratch.
The First Principles AI Workflow
A 5-step process for using AI as a Socratic thinking partner — not a code vending machine.
Describe the problem in terms of human or business outcomes — not technical systems. No solutions allowed yet.
"I have a problem where [user/business pain]. The current situation is [what is happening now]. The constraint is [time/team/budget]. Help me decompose this to fundamental requirements without suggesting solutions yet."
Ask AI to surface what you take for granted regarding scale, team, technology, and user behavior.
"Here are requirements: [paste]. What assumptions am I making about scale, team capabilities, technology constraints, and user behavior? Which assumptions, if wrong, would change the right solution?"
Explore multiple architectural paths with explicit trade-offs instead of jumping to the first plausible code snippet.
"Given these verified fundamentals: [constraints]. Generate 3 different approaches. For each, tell me: what assumptions it optimizes for, where it breaks down, and what must be true for it to be the right choice."
Once chosen, force AI to act as a skeptical senior engineer finding blind spots before building.
"I decided on [approach]. Act as a skeptical senior engineer. Give me your strongest argument against this design. What would break it in production? What hidden costs am I missing?"
Define lead indicators and instrumentation metrics to verify first-principles hypotheses in production.
"Help me define success metrics for [feature]. What should I measure to know if my first-principles reasoning was correct? What leading indicators tell me early if I need to revise?"
First Principles vs. Cargo Culting
| Scenario | ❌ Analogy Thinking | ✅ First Principles |
|---|---|---|
| Choosing a database | "Everyone uses Postgres, so we use Postgres" | Define read/write ratio, query patterns, scale needs — then choose |
| Using AI for code | Copy-paste AI output without understanding | Ask AI to explain trade-offs; verify against actual constraints |
| Architecture | "Netflix uses microservices, so should we" | Evaluate team size, deployment frequency, and boundary isolation |
| Performance issues | Add caching everywhere as default fix | Profile first. Identify the actual bottleneck. Solve that. |
| Tech stack choice | "React is popular, Kubernetes is mature" | What problems am I solving? What is the team's learning curve cost? |
| AI prompting | "Write me a user auth system" | "My constraints are X. What are fundamental auth requirements for my case?" |
Designing a Notification System
Most engineers jump straight to Firebase push notifications or message queues. First principles says: stop. Decompose first.
Occam's Razor
The simplest solution that works is usually correct. Cut unnecessary abstractions and avoid architecting for hypothetical futures.
Entities Must Not Be Multiplied Beyond Necessity
Occam's Razor states that among competing hypotheses, the one with the fewest assumptions should be selected. In software: every layer, interface, or service must earn its place.
The Occam's Razor AI Workflow
A 5-step method for applying the razor before, during, and after generating code with AI.
Strip qualifiers like "someday" and "in case we." Only what you need to ship this week counts.
"My current requirements are: [list present requirements]. Explicitly NOT in scope: [tempting future items]. Design the simplest solution meeting only in-scope items."
For every proposed component, ask what specific requirement it serves. Flag anything without a direct mapping.
"For each component in your proposed solution, answer: what specific requirement from my list does this serve? Flag anything without a direct mapping and suggest what to remove."
AI defaults to comprehensive. Always explicitly request a minimal, stripped-down version.
"Now give me a version that is1 meaningfully simpler. Drop any pattern or layer not strictly required. What are concrete trade-offs vs the previous version?"
Distinguish between essential safety (error handling, security) and premature flexibility.
"Between simpler and complex versions, separate what is missing into: 1) Essential things to add back (security/errors), 2) Speculative flexibility I only thought I needed."
Write a 1-paragraph decision record capturing why complexity was cut so future devs don't re-add it.
"Help me write a 1-paragraph decision record: 1) What we chose & why, 2) What we explicitly decided NOT to build, 3) What specific signal tells us to add complexity later."
When Engineers Violate the Razor
| Scenario | ❌ Over-Engineered | ✅ Occam's Approach |
|---|---|---|
| Auth for 3-person internal tool | OAuth2 + ID provider + refresh tokens + RBAC matrix | HTTP Basic Auth or shared secret. Add OAuth when compliance demands it. |
| Config per environment | Feature flag dashboard + SDK + percentage rollouts + A/B tracking | Environment variables. A .env file. Change value to change behavior. |
| Storing 500 static records | Redis cache + Postgres + invalidation events + TTL tuning | Query database. Add cache only when profiling proves it slow. |
| Daily background job | Celery / BullMQ + worker autoscaling + DLQ + dashboard | Cron job checking a SQL table row with run_at timestamp. |
| API for 1 internal frontend | Versioned REST API + OpenAPI spec + SDK generator | Unversioned endpoints. Change both sides together. |
| Sharing code between 2 services | Private npm package repo + CI/CD + semver + update bot | Copy the code. Refactor to a shared package when duplication hurts. |
The 6-Week vs. 1-Day Notification System
A team was asked to send email notifications for nightly reports (~200 users).
- Kafka event bus for domain events
- Dedicated microservice & separate DB
- Handlebars dynamic template editor
- User preference matrix center
- Cron job running at 6am checking SQL
- Direct SendGrid API call with text template
- Single stdout log line for observability
- Shipped before standup
Chesterton's Fence
Never remove code you don't understand. The guard or delay you can't explain is the one most likely to hurt you in production.
"An AI cleaned up the codebase on Tuesday. By Thursday, 3% of payments silently failed."
An AI assistant flagged await sleep(500); as dead code. An engineer merged the cleanup PR. Without that delay, a vendor SDK race condition surfaced under load, failing transactions silently for 6 hours.
The Archaeology Protocol
A two-phase method: investigate before touching code, then prompt with context AI can't see.
Run git log to inspect every commit touching this code. A commit message with "fix: compensate for SDK bug" is crucial context.
git log -p --follow -- <path/to/file> && git blame <path/to/file>
Search history for ticket IDs, bug numbers, and phrases like "workaround", "hotfix", or "compensate".
git log --all --grep="JIRA-ID" --oneline && git log --all -S "functionName"
Ask AI to describe what the code does and why it might exist before requesting any refactoring.
"Do not suggest changes yet. First: explain what this code does, when it runs, and what you hypothesize it was written to protect against."
Supply AI with commit messages and PR descriptions so it can reason with institutional memory.
"Here is git history and PR description: [paste]. Given this context, re-evaluate whether the code flagged is safe to remove."
Violations vs. The Archaeology Protocol
| Scenario | ❌ Violation | ✅ Protocol |
|---|---|---|
| "This looks unused" | Delete based on visual inspection | Trace all call sites including dynamic & config-driven invocations first. |
| "AI said safe to remove" | Trust AI without supplying history | AI lacks incident history; supply git blame & PR context first. |
| "Old code is bad code" | Assume 3-year-old code is obsolete | Old fences guard old wounds (e.g. vendor SDK bugs without upstream fix). |
| "No unit test coverage" | Delete because 0% test coverage | Defensive error-path code is often untested because failure is hard to mock. |
| "Don't understand it" | Delete what looks confusing | Lack of understanding is the reason to investigate, not to delete. |
| "Cleanup sprint" | Skip protocol for refactoring PRs | Apply Archaeology Protocol regardless of sprint framing. |
The 500ms That Brought Down Payments
An AI assistant flagged await sleep(500); in processor.js as dead code. An engineer merged the cleanup PR.
$ git log -p --follow -- payment/processor.js | grep -B2 -A4 "sleep" commit a3f9c2d (3 years ago) marcus.chen@company.com "fix: add delay before processor.init() call" + await sleep(500); // SDK fires PAYMENT_READY before internal state ready. JIRA-2847
Conway's Law
Your system architecture will mirror your organization's communication structures. Design your teams to produce the architecture you want.
Architecture Follows Communication
Melvin Conway observed in 1967: organizations which design systems are constrained to produce designs which are copies of their communication structures. The Inverse Conway Maneuver flips this: structure teams first, then let architecture follow.
The Inverse Conway AI Workflow
A 4-step workflow to align team structure before generating modular code with AI.
Overlay your actual team communication map onto code repositories to expose hidden shared ownership.
"Here are our team structures: [teams]. Analyze our service list [services] and map where shared ownership causes deploy coordination."
Assign single-team ownership to each business domain before writing or splitting a single line of code.
"We are reassigning domain [NAME] to Team [X] end-to-end. What API contracts must Team [X] publish for other teams to consume asynchronously?"
Treat internal cross-team calls with the same rigor as external 3rd-party vendor APIs.
"Generate an API contract for domain [NAME]. Ensure no internal DB schemas or private state leaks across team boundaries."
Scope AI prompt execution context per team domain to reinforce clean architecture boundaries.
"You are working within Team [NAME] domain. Owned services: [list]. External allowed APIs: [list]. Generate code respecting these boundaries."
How Teams Fight Conway's Law
| Scenario | ❌ Anti-Pattern | ✅ Conway-Aware Approach |
|---|---|---|
| Microservices Migration | Design architecture diagram first; force existing teams to own slices | Structure autonomous teams around business domains first; service boundaries follow. |
| Shared Service Ownership | Two teams maintain 1 monolith because "it's too big for one team" | Single team owns domain end-to-end (schema, API, deploy, on-call). |
| AI Rollout Strategy | Deploy AI org-wide without restructuring; accelerates siloed coupling | Restructure teams first, then scope AI prompt context per team domain. |
| Platform Team Model | Central platform team running on a ticket queue | Platform team provides self-service products with documented SLAs and APIs. |
The Checkout Rewrite That Wasn't
3 teams (Payments, Fulfillment, UI) co-owned a checkout monolith. A 6-month microservice rewrite failed because communication patterns never changed.
Inversion Thinking
Solve hard problems by thinking backward. Instead of asking how to achieve success, figure out how to cause catastrophic failure — and systematically eliminate those failure modes.
Invert, Always Invert
Inversion is the practice of solving problems by reversing the direction of your thinking. Popularized by mathematician Carl Jacobi ("Invert, immer invertieren") and investor Charlie Munger, inversion forces you to ask: "What would make this system fail horribly?" before asking how to make it succeed.
The Premortem & Inversion AI Workflow
A 4-step workflow to uncover critical failure modes before generating implementation code.
Assume the feature has failed catastrophically in production and prompt AI to diagnose the top causes.
"Assume this feature failed in production 6 months from now. List the top 5 technical failure modes that caused the outage."
Define explicit negative constraints that the implementation must never violate under any circumstances.
"Define 3 non-negotiable anti-goals for this system: what state or behavior must this code NEVER allow, even under network failure?"
Ask AI to play an adversarial chaos engineer to exploit race conditions and resource leaks.
"Act as a chaos engineer. Expose 5 edge cases or race conditions where this state transition model breaks."
Generate defensive guards, rate limiters, circuit breakers, and fallback mechanisms first.
"Generate defensive guards and circuit breakers for each identified failure mode before writing main domain logic."
Forward-Only vs. Inversion Thinking
| Scenario | ❌ Forward-Only Thinking | ✅ Inversion Thinking |
|---|---|---|
| Feature Launch | Focus only on happy-path user flows and expected load targets | Run a premortem to identify silent failure modes and data corruption risks beforehand. |
| Background Queue | Optimize for processing speed and batch size | Design dead-letter queues, idempotent retry guards, and backpressure limits first. |
| Security & Auth | Add basic token verification and consider it done | Actively red-team the flow: token replay, clock skew, and expired session race conditions. |
| AI Code Generation | Ask AI for the optimal implementation immediately | Ask AI to list 5 ways its proposed code will break under extreme edge cases first. |
The Silent Queue Outage Avoided
A high-growth team designed a background processing worker. An unexpected upstream API outage triggered an infinite retry loop that crashed primary database connections.
Hyrum's Law
Every observable behavior of your system will be depended on by somebody. API contracts are defined by real-world behavior, not written documentation.
Implicit Contracts Rule Production
Formulated by Google engineer Hyrum Wright: "With a sufficient number of users of an API, it does not matter what you specify on the contract: all observable behaviors of your system will be depended on by somebody." Response ordering, timing, error message text, and side effects become de facto APIs.
The Hyrum Risk Audit AI Workflow
A 4-step workflow to audit API dependencies and prevent fragile client coupling.
Identify non-contract behaviors (key order, error text, response timing) exposed by your API endpoints.
"Identify all non-contract observable behaviors (ordering, timing, un-documented fields) exposed by endpoint [URL]."
Scan client code or AI-generated callers for regex matching, string scraping, or implicit sort assumptions.
"Scan caller repository [REPO] for brittle API usage: regex parsing of JSON, string matching on error text, or missing sort parameters."
Add dev/staging middleware that randomizes unpromised ordering and key positions to catch implicit reliance early.
"Generate a dev/staging middleware that scrambles JSON key ordering and array results when explicit sorting is not requested."
Use versioning wrappers and deprecation telemetry when updating observable system behavior.
"Generate a versioning wrapper for API [NAME] that preserves legacy observable behavior while exposing clean v2 contracts."
Naive Assumptions vs. Hyrum-Aware API Design
| Scenario | ❌ Naive Contract Assumption | ✅ Hyrum-Aware API Design |
|---|---|---|
| Changing JSON Field Order | "JSON specs say key order is undefined, so changing it is completely safe" | Recognize callers may parse raw text; scramble key order in dev early to enforce real JSON parsing. |
| Database Query Refactor | "I didn't change schema fields, only the underlying SQL execution plan" | Un-ordered SQL returns non-deterministic rows; add explicit ORDER BY to guarantee contract stability. |
| Error Response Formatting | Change error string formatting assuming clients check standard HTTP status code | Preserve machine-readable error codes; document exact error schemas and telemetry. |
| AI API Integration | Assume LLM clients parse APIs strictly according to OpenAPI schema specs | Fuzz endpoints and validate against brittle regex/string parsing patterns common in LLM scripts. |
The Hash Order Incident
An infra team upgraded their backend runtime, which randomized hash map key iteration order. 14 microservices crashed because client apps assumed deterministic JSON key ordering.
Interactive Model Checkpoints
Test your engineering judgment directly within each model section or jump straight to a specific checkpoint below.
Socratic Prompt Cheatsheets
Copy-pasteable Socratic prompt templates to apply these models with AI assistants.
"Decompose [FEATURE] into fundamental requirements. List 3 assumptions I'm making about scale, team, or stack, and what would change if they were false."
"Give me the simplest architecture that meets ONLY today's requirements. What layers can we remove, and under what conditions would it break?"
"Here is git history for this file: [paste]. Before proposing changes, explain what this guard protects against and write a removal justification."
"We are structuring around team [NAME] owning services [LIST]. Generate code that respects these ownership boundaries without cross-team coupling."
"Assume [FEATURE/SYSTEM] failed catastrophically in production 6 months from now. List the top 5 technical root causes and define 3 anti-goals to prevent them."
"Analyze API endpoint [ENDPOINT/SCHEMA] for non-contract observable behaviors (key ordering, error text format, response timing) that caller SDKs or AI clients might rely on."