Don't Put the Burden on Developers
Every time you say "developers should be more careful," you are admitting that your system has a hole. Careful people still forget. Disciplined teams still drift. The answer is never "try harder" — it's "make the wrong thing impossible."
The Pattern
A bug reaches production. The post-mortem identifies the root cause. Someone writes the action item:
"Remind the team to always run tests before pushing."
Three months later, the same class of bug reaches production. New post-mortem. New action item:
"Add a checklist to the PR template."
Six months later, same bug. The checklist has 14 items. Nobody reads it. The conversation shifts:
"We have a discipline problem."
No. You have a structural problem. The system makes it easy to do the wrong thing and relies on human vigilance to prevent it. Human vigilance doesn't scale. It doesn't survive deadlines, on-call rotations, new hires, Friday afternoons, or the second year of a project.
If a failure can recur, it will recur. The only question is whether you catch it with a machine or with a human — and the machine never gets tired.
Five Structural Failures Disguised as Discipline Problems
1. "Developers Don't Run Tests Before Pushing"
The framing: lazy developers skip tests, bugs leak to main.
The reality: the test suite takes 12 minutes. Running it locally requires a specific Docker setup. Three tests flake intermittently. Developers skip the suite because the suite is hostile to them.
The structural fix:
# .husky/pre-push
npm run test:unit && SMOKE=1 npx playwright test && npm run test:compliance# .husky/pre-push
npm run test:unit && SMOKE=1 npx playwright test && npm run test:complianceA pre-push gate that runs in under 30 seconds. Unit tests, smoke Playwright on 4 curated pages, compliance check. No Docker required. No flaky tests — smoke mode tests only stable pages. If it passes, push. If it fails, the error message tells you exactly what broke.
The developer doesn't need to remember to run tests. The system runs them. The developer doesn't need discipline — they need a test suite that respects their time.
2. "Developers Don't Follow Architectural Conventions"
The framing: developers put code in the wrong layer, violate dependency rules, create circular references.
The reality: the "conventions" exist in a wiki page last updated 18 months ago. The folder structure doesn't enforce anything. Nothing prevents Presentation from referencing Infrastructure directly. The architecture is an honor system.
The structural fix:
Make the compiler enforce boundaries. In a monorepo with typed requirements, a .Requirements project defines what each feature must implement as abstract methods. A .Specifications project translates those into interfaces. The implementation project references both — and must satisfy the compiler.
// The developer literally cannot compile without implementing AC-9.
// No wiki page needed. No "remind the team" email. The build fails.
public abstract AcceptanceCriterionResult LoyaltyPointsCredited(
OrderSummary order,
CustomerId customer,
int pointsEarned,
TimeSpan elapsed);// The developer literally cannot compile without implementing AC-9.
// No wiki page needed. No "remind the team" email. The build fails.
public abstract AcceptanceCriterionResult LoyaltyPointsCredited(
OrderSummary order,
CustomerId customer,
int pointsEarned,
TimeSpan elapsed);When a PM adds an acceptance criterion, the build breaks across every project that needs to change. Not because someone filed a Jira ticket. Not because someone remembered to notify the other team. Because the type system forces the conversation.
Developers don't "forget" architecture when the compiler won't let them.
3. "Developers Don't Write Enough Tests"
The framing: coverage is low because developers cut corners.
The reality: there's no way to know which tests are missing. Coverage tools tell you which lines were executed, not which business requirements are verified. A developer can achieve 90% line coverage without testing a single acceptance criterion.
The structural fix:
Typed feature specifications with a compliance scanner. Every feature is an abstract class. Every acceptance criterion is an abstract method. Every test must reference the feature and AC it verifies. The compliance report shows, per feature, which ACs have tests and which don't.
Feature ACs Tested Coverage
─────────────────────────────────────────────────
SPA Navigation 6 6 100%
Search Engine 8 8 100%
Theme System 5 5 100%
Scroll Spy 4 4 100%
─────────────────────────────────────────────────
Total 112 112 100%Feature ACs Tested Coverage
─────────────────────────────────────────────────
SPA Navigation 6 6 100%
Search Engine 8 8 100%
Theme System 5 5 100%
Scroll Spy 4 4 100%
─────────────────────────────────────────────────
Total 112 112 100%The developer doesn't need to "remember" to write tests for each AC. The compliance scanner tells them — at build time — what's missing. The pre-push gate blocks the push if coverage drops below the threshold. The system makes the gap visible and then makes it impossible to ignore.
4. "Developers Don't Keep Quality Metrics in Check"
The framing: complexity creeps up, coupling increases, cohesion drops — because developers don't care about quality.
The reality: developers never see the metrics. The only time anyone looks at code quality is during a quarterly audit, by which point the rot is so deep that fixing it would require a rewrite.
The structural fix:
A quality gate that runs at build time. Cyclomatic complexity, cognitive complexity, coupling (Ca/Ce), cohesion (LCOM4), the main sequence distance — all computed from Roslyn syntax trees, all compared against thresholds, all failing the build if violated.
VIOLATION: MegaCorp.Orders.OrderService
Cyclomatic complexity: 47 (threshold: 25)
LCOM4: 6 (threshold: 3)
Afferent coupling: 31 (threshold: 20)VIOLATION: MegaCorp.Orders.OrderService
Cyclomatic complexity: 47 (threshold: 25)
LCOM4: 6 (threshold: 3)
Afferent coupling: 31 (threshold: 20)The developer sees the violation when they introduce it. Not three months later in a SonarQube dashboard that nobody checks. The feedback loop is immediate, actionable, and automatic.
Quality isn't a developer virtue. It's a system property.
5. "Developers Don't Document Requirements Traceability"
The framing: we can't trace which code implements which business requirement because developers don't update the traceability matrix.
The reality: the traceability matrix is an Excel spreadsheet on SharePoint. Updating it requires switching context from the IDE, finding the right row, typing a file path manually, and hoping nobody else is editing the file at the same time. The spreadsheet hasn't been accurate since week 2 of the project.
The structural fix:
Requirements as code. The feature class is the traceability matrix. The method name is the AC identifier. The test is the evidence. The compliance scanner is the audit. Everything is in the codebase, version-controlled, compiler-checked, and queryable.
No spreadsheet. No context switch. No "please update the docs." The developer writes the code, and the traceability is a side effect of the type system.
The Underlying Principle
Every example above follows the same pattern:
- A process depends on human memory, discipline, or vigilance
- Humans inevitably fail at it — not because they're bad, but because they're human
- Management responds with more process: checklists, reminders, training, reviews
- The additional process is itself another burden that requires discipline to follow
- The cycle repeats
The fix is always the same: encode the constraint in the system. The compiler, the type system, the build pipeline, the pre-push hook — these are the enforcement mechanisms. They don't forget. They don't get tired. They don't skip steps on Friday afternoon.
The left column scales with team size. More people = more reminders, more reviews, more meetings. The right column scales with the codebase. More code = more constraints enforced automatically.
"But Code Reviews Catch Everything"
No, they don't. Code reviews catch what the reviewer happens to notice. They are subject to:
- Reviewer fatigue — the 15th PR of the day gets less attention than the first
- Knowledge gaps — a reviewer can't catch a violation of a convention they don't know about
- Social pressure — rejecting a senior developer's PR feels different than rejecting a junior's
- Throughput constraints — if every architectural decision requires a senior reviewer, you've created a bottleneck that slows the entire team
Code reviews are valuable for knowledge sharing, mentoring, and catching design-level issues that tools can't see. They are terrible as the primary enforcement mechanism for repeatable, checkable rules.
If a rule can be checked by a machine, it should be checked by a machine. Save human review for the things only humans can judge.
What This Looks Like in Practice
The hardening post describes a concrete example. The visual regression suite tested every page in the sitemap. Adding a blog post — the most common workflow — broke the entire suite because the new page had no baseline screenshots.
The initial reaction might be: "developers should run --update-snapshots after adding content." That's putting the burden on the developer. The actual fix was structural:
- Smoke mode — pre-push tests only run against 4 curated stable pages, not the full sitemap
- Auto-baseline creation — new pages get baseline screenshots generated automatically
- Per-page filtering —
PAGE=/content/blog/my-post.htmltests a single page
The test suite stopped punishing the most common workflow. Developers didn't need to "remember" anything. The system adapted to how developers actually work.
The Manager's Responsibility
If you're a tech lead, an architect, or a manager, and you find yourself writing action items that start with "remind the team," stop. Ask instead:
- Can the compiler catch this? If the constraint is expressible as a type, make it a type.
- Can the build catch this? If the constraint is a threshold, make it a build gate.
- Can a hook catch this? If the constraint is a workflow step, make it a pre-push or pre-commit hook.
- Can a scanner catch this? If the constraint is a structural property, write an analyzer.
If the answer to all four is "no," then it's a process issue — and you should design the process to have as few steps as possible, with clear failure modes.
But nine times out of ten, the answer to at least one of those questions is "yes." And the structural fix will cost you a day of engineering that pays for itself every single week.
The Cost of Getting This Wrong
When you put the burden on developers, you pay in ways that don't show up in sprint velocity:
- Onboarding time — every new developer must learn the invisible rules. The wiki, the tribal knowledge, the "oh we never do that here" conventions. A structural system is self-documenting: the build fails and tells you why.
- Cognitive overhead — developers carry a mental checklist of things they must remember. Every checklist item is a context switch away from the actual problem they're solving.
- Blame culture — when failures are framed as discipline problems, the post-mortem becomes a blame session. "Who forgot to run the tests?" is a question that makes people defensive. "Why didn't the pre-push hook catch this?" is a question that leads to a fix.
- Attrition — talented developers leave environments where they're constantly told to "be more careful." They go to environments where the system handles the boring parts and lets them focus on the interesting ones.
The ROI calculation is straightforward. The monorepo ROI analysis shows $740K-$2.4M in annual waste from structural gaps in a 50-project monorepo. That waste isn't caused by careless developers — it's caused by a system that requires perfect human coordination to function correctly.
The Principle
If a developer can make a mistake, the system should make that mistake impossible — or at least visible and immediate.
Not through documentation. Not through training. Not through reminders. Through the compiler, the type system, the build pipeline, and the automation layer.
Stop putting the burden on developers. Fix the system.