Phase 01 · Week 4 · 105 minutes

Day 23: Value types, interfaces, and dependency boundaries

Modern C++, Linux, and real-time habits · Deterministic, observable software at the hardware boundary.

Chapter 04 · Production C++, Linux, and deterministic timing

Today in the field story

One problem, then the next

With ownership stable, the Night-Shift Sensor Gateway must stop passing unexplained doubles between hardware, validation, and control. Introduce values whose names preserve unit, timestamp, validity, and command meaning; place effects behind narrow adapters; and replay recorded samples through the same production calculation. A fake source tests policy, not the real device boundary.

Why now

Clear value and interface contracts prevent unit, validity, and dependency mistakes before concurrency obscures them.

Ignore today

Ignore broad plugin frameworks and universal abstractions.

Unlocks next

Small production targets that can share tested logic without copying source into tests.

Understand

Build the physical picture first

Build robot software from labelled containers and narrow plugs: each value carries one meaning, and each component exposes only the connection its neighbour truly needs.

A value type is a small object whose contents fully describe its meaning. A TemperatureCelsius, Timestamp, or WheelVelocity can be copied, compared, tested, and passed without a hidden device connection. This is safer than moving loose double values through the system, because metres per second, radians per second, and raw encoder counts should not be silently interchangeable. A constructor or factory can enforce an invariant such as finite numbers and non-negative timestamps, so invalid states are rejected near their source.

Separate calculation from effects. A filter that accepts a sample value and returns a filtered value is easy to test with a table of inputs. A serial adapter that reads bytes from Linux has an effect and may fail for timing, permission, or disconnection reasons. Put the effect behind a narrow interface, translate its result into a value, then let ordinary functions perform the calculation. This arrangement lets tests supply a recorded sensor without pretending that a fake serial port proves real hardware works.

An interface is a contract about operations and meaning, not simply a class containing virtual methods. A useful SampleSource contract says what timestamp clock it uses, which units it returns, what end-of-stream means, and how cancellation behaves. The control pipeline should depend on that contract, while a Linux serial source, a replay file, and a test source implement it. Dependency direction then points from policy toward an abstraction and from hardware-specific code toward the same boundary; high-level control rules do not import device details.

Keep boundaries small enough to reason about. A single RobotManager interface with fifty methods couples unrelated timing, storage, UI, and motor concerns. Prefer a few operations that match one responsibility, return explicit results, and make error states visible. In C++, use const for data that should not change, values when a copy is independent, references for required borrowers, and an explicit result type or exception policy agreed by the component. The goal is not maximum class count; it is preventing a change in one device driver from quietly changing control meaning elsewhere.

Words you need

Name each idea precisely

Value type

A type whose stored contents describe the complete value, without hidden identity or ownership of an external device.

Physical example:

A timestamped wheel-speed sample contains time, speed, and validity, and remains meaningful after the sensor call ends.

Invariant

A rule that must remain true for every valid object of a type.

Physical example:

A normalized duty-cycle command remains between -1.0 and +1.0.

Interface

A narrow contract describing available operations, data meaning, errors, and lifetime expectations.

Physical example:

A sample source promises timestamped speed values and a bounded cancellation path.

Dependency boundary

A deliberate seam where one component relies on a stable contract rather than another component's internal details.

Physical example:

The controller depends on SampleSource, not on USB identifiers or serial read calls.

Pure calculation

Work whose output depends only on its input values and which does not change external state.

Physical example:

Clamping a requested wheel velocity to a declared safe range.

Math, one line at a time

Work through today’s relationship

Prerequisite rescue · optionalRates, deadlines, jitter, and memory budgets

Hardware-facing software must finish work predictably before its deadline.

f
loop frequencyUnit: hertz (Hz)
T = 1/f
time available for one loopUnit: seconds (s)
jitter
variation around the expected timingUnit: milliseconds (ms)
  1. For a 100 Hz control loop, T = 1/100 s.

  2. Convert: 0.01 s = 10 ms per cycle.

  3. If work sometimes takes 13 ms, it misses the 10 ms deadline by 3 ms; measure the distribution, not only the average.

Programmer analogy

This is a strict rendering or audio-processing budget, except a missed robot deadline can destabilize an actuator.

How much time does a 50 Hz loop have per cycle?

1/50 s = 0.02 s = 20 ms.

With compatible units, queue capacity is

Brt=(100 s1)(0.20 s)=20 samples.B\ge r t=(100\ \mathrm{s^{-1}})(0.20\ \mathrm{s})=20\ \mathrm{samples}.

Because 200 ms=0.20 s200\ \mathrm{ms}=0.20\ \mathrm{s}, treating it as 200 s200\ \mathrm{s} creates the incorrect result B=20,000B=20{,}000.

Split a mixed sensor-controller function

One function opens a port, parses encoder bytes, converts counts to rad/s, filters the value, computes a command, writes the motor, and prints an error.

  1. Circle external effects: open, read, write, clock access, and logging.

  2. Define value types for raw packet, timestamped wheel velocity in rad/s, and bounded motor command.

  3. Define a narrow source contract that returns a timestamped sample or an explicit read outcome.

  4. Move parse, unit conversion, filtering, and command calculation into functions that consume and return values.

  5. Define a sink contract whose command range, timeout, cancellation, and failure result are explicit.

  6. Make the top-level pipeline compose the source, calculations, sink, and event recorder without letting calculation code access the port.

Result

Recorded samples can exercise the real calculation path, while hardware adapters remain isolated and can be tested separately at the integration boundary.

What this proves

Good dependency boundaries separate different reasons for failure while keeping the actual production logic in the test.

Physical examples

Where this appears in real life

Labelled measuring cups

Two cups both hold the number 250, but one means millilitres and the other could mean grams; the label prevents a plausible-looking substitution.

Look for:

A domain value type gives a number the same protection by carrying its unit and validity rule.

Replaceable battery connector

A tool can accept compatible battery packs because the connector's voltage, shape, polarity, and limits form a contract.

Look for:

Matching only the physical shape is not enough, just as matching a C++ method name without units and error meaning is not a complete interface.

Hands-on exercise

Make the idea observable

Create a small C++ program using only standard-library types; the input source is a fixed vector of timestamped encoder counts.

  1. Define EncoderSample and WheelVelocity structs with fields whose names and comments state units.

  2. Write one conversion function that receives a const sample reference and returns a velocity value.

  3. Reject a zero or negative time interval with an explicit result instead of dividing and continuing.

  4. Define a minimal SampleSource with one read operation and implement it with the fixed vector.

  5. Run the same conversion tests directly and through the source boundary, including wraparound or invalid-time cases.

  6. Write a dependency note listing which file may know about hardware, which may know about units, and which owns the control rule.

Observe

The numeric logic remains testable without a device, while invalid input and unit meaning are visible at type and boundary names.

Done when

The compiler prevents at least one accidental type substitution, invalid time is handled explicitly, and the control calculation contains no file, network, or device calls.

Build today

Create a Linux C++ sensor→filter→controller pipeline with device permissions, bounded timing, tests, CI, structured logs, and deterministic replay.

Evidence to save

DONE when “Value types, interfaces, and dependency boundaries” runs from one documented command and the nominal plus boundary outputs are attached.

Common mistakes

Catch the wrong mental model

Wrong

Using double for every physical quantity because the mathematical storage is the same.

Better

Use names or domain types that preserve unit, frame, clock, range, and validity so incompatible values cannot pass unnoticed.

Wrong

Calling a mock-hardware unit test an integration test.

Better

The mock can test policy against the contract; separately test the real adapter, permissions, timing, protocol, and hardware boundary.

Wrong

Creating one large interface to make every implementation interchangeable.

Better

Split contracts by responsibility and consumer need; broad interfaces increase coupling and make substitutions dishonest.

Job connection

How this becomes employable evidence

Define typed observation, command, and health boundaries so a live sensor, recorded dataset, simulator, or learned policy can connect without changing units or cancellation meaning.

Relevant target roles

  • Robotics Application / ROS 2 Integration Engineer
  • Robotics Software Engineer — ROS 2 / AMR
  • Robot Learning Deployment / Physical AI Integration Engineer

Chapter 04 interview drill

Interview questions: Value types, interfaces, and dependency boundaries

Practise a 60–90 second answer: define the idea, connect it to a physical robot, state assumptions, frames, and units when relevant, then finish with the failure signal or evidence you would inspect.

Primary interview scenario

You inherit one function that reads a camera, runs inference, and commands a robot. Show where you would place value types and interfaces, and explain which tests each boundary enables without hiding integration risk.

Answer shape: clarify the situation → trace the physical and software path → test the most likely boundaries → name the evidence that would confirm the result.

Technical follow-up questions

Q1Why is `WheelVelocity{2.0}` safer than an unexplained `double` value of 2.0?
Model interview answer

The domain type can carry the quantity's meaning, unit, validity rule, and allowed operations instead of relying on every caller to remember them.

Q2Which part should know that a sensor is connected at `/dev/robot_encoder`?
Model interview answer

The Linux or hardware adapter should know; filtering and control policy should depend only on the declared sample contract.

Q3What does replacing a hardware source with replay data prove?
Model interview answer

It can reproduce and test downstream parsing or policy behavior for those samples, but it does not prove the live device, permissions, transport, or timing works.