How structural patterns transform code review from art into engineering
Code review discussions often devolve into debates about style, naming preferences, and subjective “readability.” Two experienced developers can look at the same function and have completely opposite opinions about whether it’s “clean” or “messy.”
This subjectivity creates real problems:
What if code review could be as systematic as running a test suite?
When code follows well-defined structural patterns, something remarkable happens: functions become decomposable into discrete, analyzable parts.
Consider a function that follows no particular pattern. To review it, you must:
Now consider a function that implements exactly one pattern from a known catalog. Suddenly you can:
Patterns transform code review from holistic judgment into component analysis.
Every function implements exactly one of these patterns:
The atomic unit – a function that does one thing with no internal steps.
Business Leaf: Pure computation, no I/O
public Money calculateDiscount(Money price, double discountRate) { return price.multiply(discountRate); }
Adapter Leaf: I/O operation that bridges to external systems
public User findByEmail(String email) throws DatabaseException { try (var conn = dataSource.getConnection(); var stmt = conn.prepareStatement(FIND_BY_EMAIL_SQL)) { stmt.setString(1, email); var rs = stmt.executeQuery(); return rs.next() ? mapToUser(rs) : null; } catch (SQLException e) { throw new DatabaseException("Failed to find user by email", e); } }
A chain of 2-5 dependent steps where each step’s output feeds the next.
public OrderConfirmation processOrder(OrderRequest request) { ValidatedOrder validated = validateOrder(request); InventoryReservation reservation = reserveInventory(validated); PaymentResult payment = processPayment(reservation); return confirmOrder(payment); }
Parallel independent operations combined into a single result.
public Dashboard loadDashboard(Long userId) { CompletableFuture<UserProfile> profileFuture = CompletableFuture.supplyAsync(() -> userService.getProfile(userId)); CompletableFuture<List<Order>> ordersFuture = CompletableFuture.supplyAsync(() -> orderService.getRecentOrders(userId)); CompletableFuture<List<Notification>> notificationsFuture = CompletableFuture.supplyAsync(() -> notificationService.getUnread(userId)); CompletableFuture.allOf(profileFuture, ordersFuture, notificationsFuture).join(); return new Dashboard( profileFuture.join(), ordersFuture.join(), notificationsFuture.join() ); }
Branching based on a discriminator – if/else or switch.
public double calculateShippingRate(Order order) { if (order.isPremiumCustomer()) { return calculatePremiumRate(order); } else if (order.getWeight() > HEAVY_THRESHOLD) { return calculateHeavyItemRate(order); } else { return calculateStandardRate(order); } }
Collection processing via loops or streams.
public List<OrderSummary> getPendingOrderSummaries(List<Order> orders) { return orders.stream() .filter(Order::isPending) .map(this::toSummary) .collect(Collectors.toList()); }
Once you recognize a function’s pattern, you apply pattern-specific review criteria. This replaces vague “is it readable?” with concrete checklists.
Beyond pattern-specific checks, reviewers should watch for structural violations:
A function that starts as a Sequencer but contains an inline Fork-Join:
// BAD: Mixed patterns -- Sequencer contains inline Fork-Join public OrderResult processOrder(OrderRequest request) { ValidatedOrder validated = validateOrder(request); // Suddenly doing parallel work inline CompletableFuture<User> userFuture = CompletableFuture.supplyAsync(() -> userService.getUser(validated.getUserId())); CompletableFuture<Product> productFuture = CompletableFuture.supplyAsync(() -> productService.getProduct(validated.getProductId())); CompletableFuture.allOf(userFuture, productFuture).join(); OrderContext context = new OrderContext(userFuture.join(), productFuture.join()); return finalizeOrder(context); }
Review feedback: “Extract the parallel fetch into a separate method like fetchOrderContext().”
// GOOD: Clean -- Sequencer with extracted Fork-Join public OrderResult processOrder(OrderRequest request) { ValidatedOrder validated = validateOrder(request); OrderContext context = fetchOrderContext(validated); // Fork-Join hidden here return finalizeOrder(context); } private OrderContext fetchOrderContext(ValidatedOrder validated) { // Fork-Join pattern in its own method CompletableFuture<User> userFuture = CompletableFuture.supplyAsync(() -> userService.getUser(validated.getUserId())); CompletableFuture<Product> productFuture = CompletableFuture.supplyAsync(() -> productService.getProduct(validated.getProductId())); CompletableFuture.allOf(userFuture, productFuture).join(); return new OrderContext(userFuture.join(), productFuture.join()); }
Mixing high-level orchestration with low-level details:
// BAD: Mixed abstraction levels public void processUserRegistration(RegistrationRequest request) { // High-level step User user = createUser(request); // Suddenly low-level details String welcomeHtml = "<html><body><h1>Welcome " + user.getName() + "!</h1>" + "<p>Your account has been created.</p>" + "<a href='" + baseUrl + "/verify?token=" + user.getVerificationToken() + "'>Verify</a>" + "</body></html>"; emailService.send(user.getEmail(), "Welcome!", welcomeHtml); // Back to high-level auditService.logRegistration(user); }
Review feedback: “Extract email content generation to a separate method or template.”
// GOOD: Clean -- consistent abstraction level public void processUserRegistration(RegistrationRequest request) { User user = createUser(request); sendWelcomeEmail(user); auditService.logRegistration(user); } private void sendWelcomeEmail(User user) { String content = emailTemplates.renderWelcome(user); emailService.send(user.getEmail(), "Welcome!", content); }
Using Sequencer when Fork-Join is appropriate (sequential operations that are actually independent):
// BAD: Sequential when parallel is possible public ReportData gatherReportData(Long userId) { UserProfile profile = userService.getProfile(userId); // 200ms List<Order> orders = orderService.getOrders(userId); // 300ms AccountBalance balance = accountService.getBalance(userId); // 150ms // Total: 650ms sequential return new ReportData(profile, orders, balance); }
Review feedback: “These fetches are independent – use parallel execution to reduce latency.”
// GOOD: Parallel execution for independent operations public ReportData gatherReportData(Long userId) { CompletableFuture<UserProfile> profileFuture = CompletableFuture.supplyAsync(() -> userService.getProfile(userId)); CompletableFuture<List<Order>> ordersFuture = CompletableFuture.supplyAsync(() -> orderService.getOrders(userId)); CompletableFuture<AccountBalance> balanceFuture = CompletableFuture.supplyAsync(() -> accountService.getBalance(userId)); CompletableFuture.allOf(profileFuture, ordersFuture, balanceFuture).join(); // Total: ~300ms parallel return new ReportData( profileFuture.join(), ordersFuture.join(), balanceFuture.join() ); }
// BAD: Multiple patterns mixed together public Invoice generateInvoice(Long orderId) { Order order = orderRepository.findById(orderId); // Leaf if (order == null) { // Condition starts throw new OrderNotFoundException(orderId); } List<InvoiceLine> lines = new ArrayList<>(); // Iteration starts for (LineItem item : order.getItems()) { Product product = productService.getProduct(item.getProductId()); // Another Leaf if (product.isTaxable()) { // Nested Condition lines.add(createTaxableLine(item, product)); } else { lines.add(createNonTaxableLine(item, product)); } } double subtotal = 0; // Another Iteration for (InvoiceLine line : lines) { subtotal += line.getAmount(); } if (order.hasDiscount()) { // Another Condition subtotal = applyDiscount(subtotal, order.getDiscount()); } return new Invoice(order.getId(), lines, subtotal); }
Review feedback: “This method mixes at least 4 patterns. Extract: fetchOrder(), buildInvoiceLines(), calculateSubtotal(), applyDiscountIfPresent().”
// GOOD: Clean -- one pattern per method, composed as Sequencer public Invoice generateInvoice(Long orderId) { Order order = fetchOrder(orderId); List<InvoiceLine> lines = buildInvoiceLines(order); double subtotal = calculateSubtotal(lines); double finalAmount = applyDiscountIfPresent(subtotal, order); return new Invoice(order.getId(), lines, finalAmount); } private Order fetchOrder(Long orderId) { /* Leaf */ } private List<InvoiceLine> buildInvoiceLines(Order order) { /* Iteration */ } private double calculateSubtotal(List<InvoiceLine> lines) { /* Iteration */ } private double applyDiscountIfPresent(double subtotal, Order order) { /* Condition */ }
Before reading implementation details, identify the pattern:
CompletableFuture.allOf() or parallel streams? -> Fork-Joinif/else or switch as the main structure? -> ConditionIf you identify more than one pattern in a single method, that’s already a finding. Flag it for extraction.
Use the appropriate checklist. Check each criterion mechanically.
Read through the method body. Are all statements at the same level of abstraction? High-level orchestration shouldn’t mix with low-level implementation details.
Having a vocabulary of pattern-based feedback makes reviews faster and more actionable:
Traditional code review asks: “Is this code good?” – a question with infinite subjective answers.
Pattern-based code review asks: “Is this a valid Sequencer? Does this Fork-Join have independent branches? Is this Leaf truly atomic?” – questions with objective, verifiable answers.
When you recognize structural patterns, review transforms from art appreciation into engineering inspection. Every function has a recognizable shape. Every shape has specific criteria. Every criterion has a clear pass/fail answer.
The result: faster reviews, better feedback, and code that improves systematically rather than accidentally.
The patterns aren’t new – you’ve been writing Sequencers, Fork-Joins, and Iterations your whole career. What’s new is naming them, recognizing them explicitly, and using that recognition to make code review objective and systematic.
Want to dive deeper into pattern-based code structure? Check out Java Backend Coding Technology for a complete methodology built on these principles.