Human Complexity: It's Not Just for Psych Majors Anymore!

There are complex systems, and then there are Complex Systems. And nothing makes a system more complex than dealing with people. People make things complicated.
Let’s look at an example. I’ve recently been working on an employee time tracking tool. One of the tasks that this tool does is verification of a timesheet: are the minimum requirements met, are the project and task codes filled in correctly, and – most importantly for my point – are there at least 40 hours listed in the work week?
That last one’s simple, right?

if (entries.Sum(e => e.hours) + vacation.Sum(v => v.hours) <= 40)

Absolutely. If you’re on the standard 40-hour-per-week schedule. But some people aren’t. They’re interns or contractors or something. So, a small change.

if (entries.Sum(e => e.hours) + vacation.Sum(v => v.hours) <= 40
    && employee.Type == Types.Fulltime)

Which is great! Until someone leaves the company in the middle of a week. OK, it’s getting tricky, but we can still do that.

if (entries.Sum(e => e.hours) + vacation.Sum(v => v.hours) <= 40
    && employee.Type == Types.Fulltime
    && employee.Status == Status.Active)

Whew! We’ve got all our bases covered. Until a full time employee starts working 20 or 30 hour weeks instead of 40, or someone takes a three-day leave of absence, or HR changes the vacation policy, or someone’s manager grants them extra paid time off as a bonus, or any other number of perfectly reasonable human complications that are not accounted for.
(Note: none of these are hypothetical. The tool I’m working on has had to account for all of these and many, many more.)
So, what’s the solution? Do we keep adding validation checks every time a new possibility arises? Do we throw more and more code at the check until it’s a gigantic, unmanageable mess? And, most importantly, do we keep the users waiting while we make these changes or – even worse – give them the unsafe, unaccountable workaround of modifying data stores directly?
I say nay! And offer another solution.

if (entries.Sum(e => e.hours) + vacation.Sum(v => v.hours) <= 40)
    && employee.Type == Types.Fulltime)
    // Warning! Possible invalid timesheet.
    timesheet.Status = Status.Invalid;
    Messaging.NotifyAdministrator("Possible invalid timesheet: "
        . timesheet.FriendlyName
        . " To override and process anyway, please confirm here: "
        . BuildLinkToConfirmationPage(timesheet)

Don’t have the system freeze up when the invalid timesheet check gives a false positive. Give a user the chance to override in a friendly manner that, by still being part of the system, maintains accountability and logging. Do just enough checking to catch the majority of invalid cases, and allow an administrator to override those false positives.
By giving some of the power back to the people, you’ve decreased the complexity of your code (which increases maintainability) and increased the usefulness of the system (which decreases user frustration). According to my math, that’s a whole lot of wins.

  • David Inman

    Don’t get me wrong. I think that allowing human intervention as part of your problem solution is an incredibly clever solution. In fact I believe that some [1] are even trying to use it in order to solve otherwise intractable problems.
    However, I’ve had some very promising results leveraging monads [2] to help deal with this type of complexity in a related problem space.
    Typically, you only see monads mentioned in programming languages that are more academic in nature (or in abstract mathematics). However, there’s some areas in software engineering where monads really lend a hand. I’ll have to write a blog entry describing some of my experiences.
    [1] –
    [2] –

  • Derek Plote

    This seems like a great place to use the Strategy Pattern.

Add your comment

Your email address will not be published.