Software Architecture

One Board, Three Modes: Sharing Behavior Without Duplicating State

A mode-driven board screen can share engine hooks and state across analysis, editor, and setup while keeping mode-specific behavior configurable.

Octacer July 29, 2026
A phone held in one hand showing a chessboard above a compact controls panel, rendered as clean geometric shapes with a single small green accent and no readable text.

The same business screen, rebuilt three times. One version for analysis, one for editing data, one for setup. Same fields, same validation rules, same save logic — but three separate code paths, three separate bug surfaces, and three places to apply every future change.

This pattern is common in internal operational tools. A board that tracks inventory, sales pipeline, or project status needs to display the same underlying data in different contexts. The fastest way to ship it is to copy the screen and adjust. The result is a maintenance trap that gets more expensive with every feature added.

Why duplicated screens become expensive

Every duplicated screen means slower releases, more regression testing, and higher maintenance overhead. Consider a concrete example: a change to a validation rule — say, a field that now requires a minimum value — must be applied in the analysis view where users read the data, the editor view where they modify it, and the setup view where they configure it. If the rule lives in three copied components, the change is three edits, three review cycles, and three opportunities to introduce a discrepancy. A bug that slips into one copy produces a board that accepts a value in setup, rejects it in the editor, and displays it without warning in analysis. Users experience this as a system that "doesn't know its own rules."

The cost is not just engineering effort. It is behavioral drift: the same data presented inconsistently across modes erodes trust in the tool. When operators cannot tell which screen reflects the real state, they start manually reconciling — which is exactly the kind of predictable, repetitive coordination that automation and well-designed systems are meant to remove.

Mode as configuration, not as a screen

The core reframing: a mode is a configuration, not a screen.

Think of the engine as a car's chassis and each mode as a different body style — same platform, different shell. The chassis carries the structural integrity: the state, the data-loading logic, the validation rules, the save and cancel behaviors. The shell determines what the driver sees and is allowed to touch: which fields are visible, which actions are enabled, which headers and hints are shown.

Applied to a board screen, this means the mode — analysis, editor, setup — affects presentation and permitted actions, not the underlying data model or its rules. The board's state (what items exist, what fields they have, what state transitions are allowed) is shared. The mode changes:

This is the smallest credible solution. It does not require abstracting the entire application into a plugin architecture. It requires identifying the shared heart of the screen and keeping the mode-specific differences at the edges, configured rather than copied.

  • which fields render
  • whether controls are read-only or editable
  • which action buttons appear
  • which validation rules are enforced client-side vs. deferred to the server

How a mode-driven board works

A typical implementation separates the screen into three layers.

The shared state and engine

  1. 1

    Shared state engine

    The board's data and behavior live in a single engine — a hook or controller that owns the item list, the field definitions, the selection state, and the operations that mutate them. This engine does not know which mode it is running in. It exposes the same operations regardless: load, update, validate, save, cancel.

  2. 2

    Mode configuration

    Each mode is a plain configuration object describing what differs. No component logic, no state — just declarations.

  3. 3

    Rendering component

    One component consumes the engine and the mode config. It maps the configuration to UI:

function useBoardEngine(config: BoardConfig) {
  const [items, setItems] = useState<Item[]>([]);
  const [selection, setSelection] = useState<Set<string>>(new Set());

  const validate = (item: Item) => applyRules(item, config.rules);
  const updateItem = (id: string, patch: Partial<Item>) =>
    setItems((prev) => prev.map((it) => (it.id === id ? { ...it, ...patch } : it)));

  return { items, selection, validate, updateItem, save, cancel };
}

The engine is mode-agnostic. It does not care whether the caller is displaying data, editing it, or configuring the board — it only guarantees that all three modes operate on the same state and the same rules. That single guarantee is what eliminates drift.

The mode configuration

const MODES = {
  analysis: {
    editable: false,
    visibleFields: ["name", "owner", "status", "value"],
    actions: ["export", "refresh"],
  },
  editor: {
    editable: true,
    visibleFields: ["name", "owner", "status", "value", "notes"],
    actions: ["save", "cancel"],
  },
  setup: {
    editable: true,
    visibleFields: ["key", "label", "type", "required"],
    actions: ["save", "cancel", "addField"],
  },
};

This is the entire difference between modes. Because the configuration is data, it can be inspected, tested, and even changed at runtime without touching component code. Adding a new field to the editor but not the analysis view is a one-line change to a config, not an edit to a component tree.

The single rendering component

function BoardScreen({ mode }: { mode: Mode }) {
  const config = MODES[mode];
  const board = useBoardEngine(config);

  return (
    <div>
      {config.visibleFields.map((field) => (
        <Field
          key={field}
          name={field}
          value={board.items[0]?.[field]}
          editable={config.editable}
          onChange={(val) => board.updateItem("0", { [field]: val })}
        />
      ))}
      {config.actions.map((action) => (
        <ActionButton key={action} action={action} board={board} />
      ))}
    </div>
  );
}

The mode becomes a prop. The engine provides state and operations; the config controls presentation; the component renders whatever combination arrives. There is one validation path, one save path, one state model — and three modes that share them.

What good looks like

When this approach is working, the observable signals are clear:

  • A change to a validation rule or save behavior is a single edit, not three.
  • Regression testing shrinks to a materially smaller surface. Instead of verifying identical logic in three contexts, you verify the shared engine once and confirm the configs render as expected. Other factors can still require multi-context testing, but the duplicated-logic burden largely disappears.
  • New modes are cheap to add. Need a read-only "review" mode? Add a config entry — no new screen.
  • Debugging is simpler. A bug is either in the engine (affects all modes) or in a config (affects one), which is a much easier distinction than tracing the same bug across three copied components.

There is also a less visible benefit: the mode configuration becomes a place where product and business rules live as data. That makes them auditable and testable independent of UI code.

Tradeoffs and when not to use this approach

The mode-driven approach is not universally correct, and it is worth being specific about its limits.

The main tradeoff is that deeply different modes can strain the single-component model. If one mode is a dense table and another is a visual kanban board, forcing both through one rendering component produces awkward conditional logic. In that case, the shared engine is still valuable — share the state and rules, but allow different components to consume the same engine. The configuration principle does not require a single component; it requires a single source of truth for state and behavior.

The approach is also wrong for screens that merely look similar but represent genuinely different domains. Two screens that happen to show a list of items but mutate unrelated data models are not modes — they are different features that share a visual style. Forcing them into one engine couples things that should stay independent.

A practical first step

If your internal tools have three screens that show the same board in different contexts, there may be an opportunity to consolidate. The quickest diagnostic is to map the workflows: list what each screen loads, validates, saves, and displays, and note where the logic overlaps. Where the overlap is complete — same state, same rules — a mode-driven engine removes the duplication. Where it is partial, the mapping tells you whether the shared part is worth extracting.

Worth mapping your workflow to find where duplicated screens are multiplying your release and testing costs? Octacer's business-first diagnostics can identify consolidation points before they become unmanageable — and, where the shared core is clear, build the mode-driven engine that eliminates the drift.

Ready to Implement These Strategies?

Let's discuss how to apply these insights to your specific business challenges.

Schedule Consultation