Compliance is a critical piece of Samsara that our customers rely on to ensure the safety of drivers in their fleet and the ability to operate according to local regulations. Compliance in this case refers to specific laws governing the number of hours drivers can work in a given time period before they’re required to take a break. Our customers need to report working hours in the event of a government audit, as well as ensure drivers are in compliance with regulations in real-time. Providing tools to track working hours in-real time ensures that our customers can efficiently manage their fleet and that drivers take breaks to remain safe on the road.
Underpinning these tools that help fleets stay compliant with government regulations is a rules engine — a complex logical framework that processes working time and determines whether infringements have occurred. This post recaps our experience developing a rules engine for the EU market when we were first entering it, balancing out an MVP that we could ship quickly while leaving room in our design to account for the uncertainties of a new market.
The tachograph and drivers’ working hours in the EU
In the EU, “Drivers’ working hours” regulations define the number of hours drivers are allowed to work before taking a break. In order to stay compliant with these regulations, certain trucks are required to be outfitted with a special hardware device called a tachograph which automatically tracks drivers’ working hours. When a driver begins a shift, they insert their driver card into the tachograph. The tachograph interfaces with the vehicle to determine when the vehicle was in motion, and records how long a driver was working or resting. At the end of a shift, a driver will eject their card. With the data collected by the tachograph, it’s possible to get a complete historical view into the working hours for a particular driver.
In the event of a government audit, fleets need to be able to provide a history of records collected by the tachograph. One challenge our customers face includes regularly downloading tachograph records, which traditionally required a manager to physically interact with the tachograph. If a manager has a fleet consisting of tens or hundreds of vehicles, this could become a cumbersome process. When we first launched in the EU, we built out support for remote tachograph downloads, which allowed our vehicle gateways to automatically retrieve and store data from the tachograph.
From reactive data to proactive insights
At Samsara, we love shipping new features and rapidly iterating based on customer feedback. With remote tachograph downloads as an MVP, we had helped our customers meet the fundamental need for a tool to help with compliance for government audits. As a next step, our customers were interested in seeing real-time tachograph data to proactively prevent infringements and coach driver behavior. With live visibility into remaining drive time, fleets can optimize routes and dispatch drivers that have remaining drive time without fear of exceeding the allowed drive time.
Implementing proactive tools like remaining drive time meant understanding and codifying regulations around drivers’ working hours.
Fundamentally a modeling problem
Our Product Manager helped compile and prioritize an initial list of infringements we wanted to surface to our customers. Initially, we wanted to focus on a set of core infringements common to the EU. However, we knew that as we would continue to expand to new countries, we would encounter special cases and exceptions applicable to smaller regions.
To give an example, some of these rules include:
Daily driving period shall not exceed 9 hours, with an exemption of twice a week when it can be extended to 10 hours.
Total weekly driving time may not exceed 56 hours and the total fortnightly driving time may not exceed 90 hours.
Daily rest period shall be at least 11 hours, with an exception of going down to 9 hours maximum three times a week. Daily rest can be split into 3 hours rest followed by 9 hour rest to make a total of 12 hours daily rest
Breaks of at least 45 minutes (separable into 15 minutes followed by 30 minutes) should be taken after 4 ½ hours at the latest.
(Source: European Commission)
Even after re-reading these rules several times, they are still a mouthful! But if we know what we want to build, what remains is a modeling problem. How do we model legal text in code so that we can create a rules engine that determines whether violations have occurred given an input of times drivers were working?
After some substantial paraphrasing and working through three of these rules, we can see some common themes, highlighted in the figure below:
Each rule encodes at least one pair of duration and state. In other words, rules care about tracking an amount of time elapsed while in a certain state. Some rules may track the interaction between multiple duration/state sets.
Some rules act over a certain period of time. This means that we care that the duration/state pair occurred over a given window of time (ex. week).
Some rules have extra features. For example, the rule above contains a count. This necessitates some degree of flexibility in our final rules engine design.
Now, we see that rules share a similar structure. We can exploit this structure as a basis to build out a configurable rules engine.
The world through the lens of time & duration
As we saw, rules generally encode relationships between duration/state pairs. It just so happens the tachograph gives us exactly this data! For a particular driver, the tachograph records:
The state the driver was in: driving, resting, or working
The time the driver entered a particular state
The duration the driver was in a particular state
We will call this (state, time, duration) tuple a log. Logs will serve as an input to our system. This means that our system will then have to track time in particular states to determine if infringements have occurred.
Let’s introduce the next abstraction that will help us track this information, a gauge. Think of a gauge as a counter; when provided with a log, the gauge will increment according to the state and duration of the log. The time can be used to determine if a gauge should reset.
As an example, “cannot exceed 56 hr driving in a week” can be modeled with a single gauge.
Each driving log will increment the gauge.
If more than 56 hours worth of driving has occurred, then the gauge will overflow and an infringement will occur.
At the end of every week, the gauge will reset.
Each rule can then be modeled as operating on an input log through a series of gauges connected together. For example, a rule like “Required 45 min rest after 4.5 hr drive time” requires us to track two durations and encode a dependency between resting and driving. We then have a basic rules engine where a log is passed through a sequence of gauges, and infringements are outputted if gauges overflow. Using a domain-specific language (DSL), we can express these rules as a relationship between gauges.
Key takeaways for a complex problem
A similar DSL-based approach to a rules engine has been used at Samsara for close to 2 years now, with extensions to support new functionality like location-based rules and country-specific rules. Opting for a more open-ended framework allowed these extensions without requiring a full rewrite every few weeks. Along the way, we’ve learned a few lessons.
Designing for exceptional cases
Although most rules readily reuse this pre-built framework, there are inevitably exceptions. Each of these exceptions requires some thought, especially if they require piping new data through the rules engine. When creating the framework, designing escape hatches for these exceptions makes it easy to handle one-offs without increasing the complexity of the underlying rules engine. Here, the composability of the rules engine ensures that we could support one-offs without affecting other rules.
DSLs are a complexity tradeoff
Overall, DSLs are hard to reason about — there’s a lot of context required to onboard and maintain the underlying framework. As functionality has expanded, increased DSL complexity exacerbates the onboarding penalty. In order to ensure the framework remains easy to use, it’s important to carefully consider the design of a new feature. This includes understanding why it’s being added, deciding whether it’ll be useful for future rules, and determining how it interacts with previously added features.
Unit tests allow iteration without fear
One immutable truth we’ve found along the way is that defining behavior through test coverage is key. Early on, we built a test framework that makes it easy to visualize a sequence of logs and convert them into test cases. Modular components used in the framework allowed us to ensure test coverage for core functionality. With confidence in our underlying system, integration tests could focus on the correctness of each individual rule. Equipped with comprehensive unit and integration tests, we can iterate without fear.
Building for the long term
We’ve explored alternative solutions but ultimately traded off some increased complexity for the ability to retain flexibility for future expansion. Some notable advantages of our approach include:
We are able to reuse a well-tested foundation. Because rules share a similar structure, we only have to implement the complex temporal logic once. With each new rule, previously built components can be linked together differently.
We also get clarity in isolation. Rather than one large intermingled for-loop, the DSL allows us to isolate different rules or compose multiple rules together to form a ruleset. This allows multiple developers to work on implementing rules in parallel, without concern about unexpected interactions.
Expanding into a new market can be a daunting challenge. With some engineering tradeoffs along the way, the rules engine has been expanded to implement region-specific rules in Germany. As we continue to expand to new countries, having a configurable rules engine ensures that we can continue to iterate on a compliance solution that our customers depend on day-to-day.