The importance of terminology

2/1/2020 in warm-and-fuzzy

I've finally made it through Step 1 of the master plan:

  1. Build a replacement thermostat platform that is IoT-enlightened (but otherwise functionally identical) to make real-time temperature data available

That only took nearly a year, though in fairness, I also got to:

  1. Build basic mobile app (and web app, for bonus points)

With all of this massive progress achieved (only 67% of the todo list remain!), it's time to journal what I've learned over a handful of posts.

Terminology!

Why I care

I start most projects by first nailing down terminology. The value of doing this cannot be understated. For one, having to rename stuff partway through a project can be distracting, time-consuming, and merge-conflict-inducing. More importantly, though, I find that if I cannot nail down the terminology, I don't understand the design well enough to build it.

At the beginning of WarmAndFuzzy I didn't have a particularly great grasp on the technologies I was using (and also changed tech partway through) so a bunch of changes were required. That's okay for an experimental project in the beginning but it was worth getting it straightened out because there's nothing worse than coming back to a side project you haven't touched in weeks (or months) and finding yourself confused by misaligned or ambiguous word choices that you're now having to trace back and understand.

As of today, 10% of the 842 commits to the project are "gardening" commits that improve naming, formatting, or tech debt without any (intentional) side effects. It's a major morale boost to get to do that sort of work whenever I feel like it, and to get to come back to a nice codebase whenever I resume work.

Show me what you got

The physical | functional antonym'ing below is shamelessly stolen from Windows NT.

Let's get physical

Let's get functional

Sensors also have a configuration that defines operational traits for sensors (chiefly its display name).

As such, we have the convention that normal people using the system on a day-to-day basis only care about (and see) settings while a system administrator (i.e. me) also sees configuration, and that convention is applied consistently to thermostats and sensors (and whatever else may come in the future).

It matters less that these terms are entirely intuitive (which is hard to achieve) and more that they are consistent and that antonyms are cleanly established and maintained.

As we'll see later, the settings vs. configuration divide projects cleanly into maintaining different tables (e.g. thermostat settings vs. configuration), different GraphQL types, different sections of the UI, and different authorized privileges granted to user accounts to control who gets to change day-to-day system settings (everyone in the house) and who gets to see and make administrative changes to the system's configuration.

With the terminology set up cleanly, you can look at any piece of code and understand right away whether something is targeting normal people or administrators without having to further understand what it is. Neat.

What would you say your value is?

Each device configuration specifies the name of the stream its values should be saved into. Making the stream name configurable independent of the device name and/or ID allows us to:

Who are you?

Every device has an id, a string assigned by whatever manufactured the device (Particle for thermostats, DalSemi/Maxim for temperature sensors).

Where do you live?

I built the system as a multi-tenant architecture from the beginning because, plainly, it was easy.

When a device contacts the API, we need to look up what tenant we should route its data into, so there is one device tenancy table with that mapping.

Behind the scenes

Every term discussed so far is visible in the user interface (at the very least for administrators). Every term is used directly, without change, abbreviation, or change in pluralization in the code.

There are a few additional terms I use consistently in or around the code:

Lessons learned

void applyTimezoneConfiguration()
{
    //
    // We don't bother telling Particle about DST, we'll just change zones (e.g. PST to PDT)
    // so the above is sufficient. We just need the math to work, we don't need nice formatting.
    //
    // Our configuration tells us the current timezone's offset as well as
    // the next one and when to switch over, in case we don't update frequently.
    //

    bool const inNextTimezone = g_Configuration.rootConfiguration().nextTimezoneChange() <= Time.now();

    // {current,next}TimezoneUTCOffset: signed IANA UTC offset, e.g. PST = 480
    int16_t const timezoneUTCOffset = inNextTimezone ? g_Configuration.rootConfiguration().nextTimezoneUTCOffset()
                                                     : g_Configuration.rootConfiguration().currentTimezoneUTCOffset();

    // particleTimezone: signed fractional hours, e.g. PST = -8.0
    float const particleTimezone = -1.0f * (timezoneUTCOffset / 60.0f);

    // Apply
    Time.zone(particleTimezone);
}

Pretty much everything mentioned here is a hard-earned lesson, be it on this project or on previous ones.

Grumpycorp Studios
Seattle, Washington