← Software design practices guides

How to prevent duplicate logic with specs

How-To Software design practices Intermediate 1101017HOWTO-1101017

HOWTO-1101017Software design practicesIntermediate

This guide shows you how to prevent duplicate logic with SpecDD in a spec-driven development workflow.

Duplicate logic usually starts as a practical shortcut. A UI component repeats a validation rule to show immediate feedback. An API handler repeats the same rule to protect the backend. A job repeats it because it runs outside the request path. Later, the rule changes and one copy stays wrong.

SpecDD prevents this by making rule ownership explicit. One spec owns the rule. Other specs can depend on, reference, or call that owner, but they should not silently copy the logic.

Short answer

When you find duplicated behavior, choose the spec that should own it. Put the durable Must, Scenario, Example, or contract rule there. In other specs, use Depends on or References to point to the owner and Must not to block secondary implementations. Update tests so the rule is proven at the owner and callers test their integration with it.

When to use this guide

Use this guide when:

The design idea

The best duplicate-logic fix is not always “extract a helper.” Sometimes the right design is a policy, model, service, adapter, formatter, or API contract. The important first step is ownership: which spec owns the rule, and which specs only use the rule?

Specs make duplication reviewable. If two specs state the same behavior, reviewers can decide whether the behavior is truly shared parent behavior, local behavior owned by one spec, or accidental duplication that should be removed.

Steps

1. Find duplicated behavior

Look for repeated rules such as:

Then search specs for duplicate Must, Scenario, or Example entries.

2. Choose one owner

Ask:

For a business rule, the owner might be a model, policy, or domain service spec. For response shape, it might be an API spec. For external translation, it might be an adapter spec. For presentation-only formatting, it might be a component or UI utility spec.

3. Move rules to the owner

Owner:

Spec: Itinerary Validation

Purpose:
  Decide whether an itinerary item can be saved.

Must:
  Reject itinerary items without a place name.

Do not keep the same rule in UI and API specs unless those specs own distinct behavior around the result.

4. Make callers depend on the owner

UI spec:

Depends on:
  ItineraryValidation

Must:
  Show validation feedback returned by itinerary behavior.

Must not:
  Duplicate itinerary validation rules.

API spec:

Depends on:
  ItineraryValidation

Must:
  Return a validation response when itinerary validation rejects input.

Must not:
  Reimplement itinerary validation rules.

The UI and API can react to the rule. They do not own the rule.

5. Forbid secondary implementations

Use Must not for behavior duplication:

Must not:
  Duplicate missing-place validation in UI components.

Use Forbids for bypass paths:

Forbids:
  direct access to itinerary validation internals from UI

This prevents callers from depending on a private implementation instead of the stable rule owner.

6. Adjust tests

Move detailed rule tests to the owner:

Done when:
  Missing-place validation is covered in Itinerary Validation checks.

Callers should test integration:

Done when:
  The API returns a validation response when ItineraryValidation rejects input.
  The form shows validation feedback returned by itinerary behavior.

This avoids copying the same rule test in every layer while still checking each caller’s responsibility.

Example duplicate-logic cleanup

Before:

Spec: Itinerary Form

Must:
  Reject itinerary items without a place name.
Spec: Add Itinerary Item API

Must:
  Reject itinerary items without a place name.

After:

Spec: Itinerary Validation

Must:
  Reject itinerary items without a place name.
Spec: Itinerary Form

Depends on:
  ItineraryValidation

Must:
  Show validation feedback returned by itinerary behavior.

Must not:
  Duplicate itinerary validation rules.
Spec: Add Itinerary Item API

Depends on:
  ItineraryValidation

Must:
  Return a validation response when itinerary validation rejects input.

Must not:
  Reimplement itinerary validation rules.

The rule now has one owner and two callers.

Common mistakes

How to verify the result

Duplicate logic is controlled when:

← Software design practices guides