Next Steps
Module 13 · Architecture for Agent-Assisted Work /Lesson 13.1 /5 min

Which trade-offs actually shifted

Most architectural advice survives unchanged. Four trade-offs genuinely moved, and knowing which matters more than any new principle.

There is a temptation to declare that everything about software design has changed. It has not. Cohesion, coupling, clear boundaries and good names are still what makes a system workable, for exactly the same reasons.

Four specific trade-offs have moved, though, and they moved because the cost of writing code fell while the cost of understanding it did not.

1. Duplication versus indirection

Shifted toward tolerating duplication. The classic argument for extracting a shared abstraction was partly that writing the second copy was expensive. It is not any more. Meanwhile the cost of indirection — following an abstraction across five files — is paid by every reader, human and agent, on every visit.

This does not license copy-paste everywhere. It shifts the threshold: three similar-but-not-identical implementations that each read top to bottom are now often better than one parameterised abstraction with five options, where they were previously a clear smell.

2. Explicit versus implicit

Shifted hard toward explicit. Convention-over-configuration frameworks rely on knowledge that exists nowhere in the repository. A human learns the convention once; an agent re-derives it every session from whatever is in context, and gets it wrong when the convention is unusual.

Explicit wiring costs lines and saves an entire category of confident wrong guesses. The same applies to magic: decorators that do three things, metaprogramming, implicit registration by file location.

3. Cleverness versus searchability

Shifted hard toward searchability. An agent finds code by grep. A dynamically constructed method name, a handler registered by convention, a string assembled at runtime — all of these are invisible to search, and therefore effectively invisible.

the difference in practice
// invisible: grep for "handleOrderCreated" finds nothing
const handler = handlers[`handle${pascalCase(event.type)}`]

// findable
const handler = {
  'order.created': handleOrderCreated,
  'order.refunded': handleOrderRefunded,
}[event.type]

4. Test coverage versus test quality

Shifted toward quality, sharply. Coverage was always a weak proxy. Now that generating tests is nearly free, coverage is trivially achievable and says almost nothing, while the value of tests that genuinely discriminate correct from incorrect has gone up — because they are what lets you delegate.

What did not shift

  • Boundaries and cohesion. Still the main determinant of whether a system stays workable.
  • Simplicity. Cheap generation makes complexity cheaper to produce and no cheaper to live with.
  • Naming. If anything more important, since names are a large part of what an agent has to reason from.
  • The cost of a bad data model. Unchanged and still the most expensive mistake available.
  • Deleting code. Still the best refactor, and now more valuable because dead code becomes context.

The one-sentence summary

Optimise for a reader who is fast, tireless, has no memory between visits, and can only see what is in front of them. That description fits an agent, and it also fits a new engineer on their first week — which is why almost everything in this module is good design advice that happens to have become more urgent.

Exercise

Find the most clever piece of code in your system — dynamic dispatch, metaprogramming, convention-based registration. Try to locate its behaviour using only grep, as an agent would.

Time how long it takes. Then decide honestly whether the cleverness is paying for what it costs every reader, every time.

Worked solution

The candidate: an event handler system using convention-based registration by filename.

the grep test
Question: "what happens when an order.refunded event arrives?"

grep -r "order.refunded" .
  -> 3 hits, all in test fixtures and one JSON schema. No handler.

The handler lived in src/events/order/refunded.ts and was registered by
a loader that walked the directory and derived the event name from the
path. The string "order.refunded" appeared nowhere in the handler.

Time for me, knowing the system: 4 minutes.
Time for an agent, no prior knowledge: it never found it. It reported
that no handler existed and proposed writing one.
what it cost, measured over two weeks
Three separate sessions in which an agent concluded a handler did not
exist and proposed creating a duplicate. Two were caught in review. One
was not, and shipped a second handler for order.created which ran
alongside the first for nine days, double-sending a customer email.
the change
// src/events/registry.ts - one file, explicit, greppable
export const HANDLERS = {
  'order.created':  handleOrderCreated,
  'order.refunded': handleOrderRefunded,
  'order.shipped':  handleOrderShipped,
  // ... 31 entries
} satisfies Record

The loader was deleted. 31 lines of explicit registration replaced 40
lines of directory-walking magic.

Bonus, unplanned: the `satisfies Record` made it a
compile error to have an event type with no handler. The old system
silently dropped unhandled events, which it turned out had been
happening for one event type for four months.

Thirty-one lines of boring, repetitive, explicit code replaced a clever loader, made the system greppable, and surfaced a four-month silent bug through the type system. The duplication-versus-indirection trade-off and the explicit-versus-implicit one both pointed the same way, and the compile-time exhaustiveness was free once the mapping was a value rather than a convention.

Takeaways

  • Four trade-offs shifted: duplication over indirection, explicit over implicit, searchable over clever, quality over coverage.
  • Boundaries, cohesion, simplicity, naming and data modelling did not shift at all.
  • Optimise for a reader who is fast, tireless, has no memory, and sees only what is in front of them.
  • Explicit registration as a typed value gives you exhaustiveness checking for free.

Check yourself

Why is convention-based registration by filename particularly costly now?

A course by