Most code reviews are theater. A developer submits a pull request, colleagues sprinkle comments about missing semicolons or inconsistent naming, someone approves, and the code ships. The ritual is complete, but the design remains untouched—often quietly degrading with each passing merge.
This is a profound waste. Code review is one of the few moments when multiple experienced engineers deliberately examine the same piece of software. It's an opportunity to shape architecture, catch conceptual mistakes before they calcify, and transfer design knowledge across a team. Yet most teams squander it on concerns that a linter could handle in milliseconds.
The problem isn't laziness. It's that reviewing for design is genuinely harder than reviewing for style. It requires reading intent, imagining future changes, and articulating trade-offs. But teams that learn to conduct design-focused reviews consistently produce better software—and better engineers. What follows is a framework for making every review count.
The Questions That Uncover Design
Great reviewers don't scan for errors. They interrogate structure. The difference lies in the questions they carry into every review—questions that force both author and reviewer to think about the shape of the code, not just its correctness.
Start with responsibility. Does this class or function have a single, clear reason to exist? When a reviewer sees a service method that fetches data, transforms it, logs metrics, and dispatches events, the appropriate question isn't whether it works. It's whether these concerns belong together. Names often betray the truth: if you cannot describe what something does without using the word 'and,' its responsibilities have already fractured.
Then examine abstraction. Is this code operating at a consistent level? A function that mixes high-level business logic with low-level string manipulation is a signal of missing abstraction. Ask whether the reader must constantly shift mental gears to follow the flow. Ask whether details that should be hidden are leaking through interfaces they shouldn't cross.
Finally, probe for flexibility—carefully. Not every piece of code deserves extension points. But the right question is: what change is likely to arrive next, and how much pain will this design cause when it does? If the answer reveals that a trivial future requirement would demand rewriting half the module, the current design is quietly borrowing against tomorrow.
TakeawayGood design questions aren't about finding fault—they're about surfacing the invisible assumptions that determine whether code will age gracefully or become a liability.
Feedback That Teaches Instead of Wounds
How you say something in a code review shapes what happens next. A comment that reads 'this is wrong, use a factory here' triggers defensiveness. The same insight, phrased as 'I'm curious about the construction logic—would a factory make the intent clearer as this grows?' invites dialogue.
The distinction is not about softening critique for its own sake. It's about respecting the reality that the author has context you lack. They may have already considered your suggestion and rejected it for good reasons. Framing feedback as questions or observations rather than verdicts creates space for that context to emerge.
Effective design feedback also explains the why. Saying 'extract this into a separate class' is an instruction. Saying 'this method is handling both persistence and validation—separating them would let us test the rules without a database, and change storage without touching business logic' is education. The second form transfers a principle the author can apply on their own next time.
Distinguish between preferences and principles. Not every design opinion deserves equal weight in a review. Label suggestions honestly: 'nit' for stylistic preferences, 'consider' for genuine alternatives worth discussing, 'blocking' for issues that must be resolved. This taxonomy prevents reviews from devolving into power struggles over taste, and keeps attention focused on decisions that actually matter for the software's future.
TakeawayThe goal of a review comment isn't to demonstrate that you're right. It's to leave both the code and the author better than you found them.
Reading the Signals Across Many Reviews
Individual reviews catch individual problems. But patterns across many reviews reveal something more important: the systemic weaknesses of a codebase or the blind spots of a team. Reviewers who pay attention to these patterns become architects, whether or not that's their title.
Watch for recurring comment types. If reviewers repeatedly ask 'where does this belong?' across different pull requests, module boundaries are unclear. If the same clarifying questions arise about a particular subsystem, that subsystem lacks a coherent mental model. If tests are consistently hard to write, the design is fighting testability at a structural level.
Pay attention to what reviews miss. When production incidents trace back to code that passed review, the question isn't 'who failed?' but 'what did our review process not equip us to see?' Perhaps the reviews focused on local correctness while missing cross-cutting concerns. Perhaps reviewers lacked context about the broader system. These are process problems, not personal ones.
Escalate patterns into structural conversations. A single awkward pull request should be discussed in the review. A dozen pull requests exhibiting the same friction should prompt a design meeting, a refactoring initiative, or a documented convention. Reviews are the immune system of the codebase—but immunity requires memory. Teams that track recurring issues and act on them turn individual observations into architectural evolution.
TakeawayThe friction you feel repeatedly during reviews is the codebase trying to tell you something structural. Listen to the pattern, not just the instance.
Code review earns its place in the development process only when it addresses what tools cannot. Formatters handle style. Linters catch bugs. Static analyzers enforce rules. What remains—the questions of responsibility, abstraction, and long-term flexibility—is uniquely human work, and it demands human attention.
Teams that treat reviews as design conversations rather than compliance checks build software that ages differently. Their codebases accumulate wisdom rather than debt. Their engineers grow faster because they receive substantive feedback on the decisions that actually matter.
The next review you conduct is an opportunity. Ask better questions. Frame your feedback to teach. Notice the patterns. The compounding effect over years is the difference between a codebase you tolerate and one you're proud of.