DEV Community

Rafal Stozek
Rafal Stozek

Posted on • Updated on

Locality of behavior

There is a new trend gaining traction lately which is called “Locality of Behaviour principle”. It is a great new name for an old concept - cohesion. It’s growing lately around HTMX and TailwindCSS communities, but it’s an useful concept in general.

⚠️ Let me begin with a warning first: I don’t believe in “principles” when it comes to software engineering. I think of everything as techniques that - when used correctly and in the right context - make the software better in some aspect (eg. easier to understand). But when used without understanding or overused - they make it worse. There are no silver bullets and Locality of Behaviour isn’t one either.

Here’s what it is about:

The behaviour of a unit of code should be as obvious as possible by looking only at that unit of code.

This points back to a quote from a book by Richard P. Gabriel which states that:

The primary feature for easy maintenance is locality:
Locality is that characteristic of source code that enables a programmer to understand that source by looking at only a small portion of it.

Ease of understanding

LoB (Locality of Behaviour) is mostly concerned with ease of understanding. It’s much easier to understand code which lives together and can be easily and quickly tracked than code where you need to jump around the whole codebase to piece the logic together.

It’s easy to imagine: let’s say that you are working on a backend app - not an API one, but a Multi-Page Application, server-side rendered. Now, to implement a new functionality you need to:

  • modify model a bit
  • change how controller works
  • update a form class
  • change the template
  • modify styles a bit

Which one will be easier to update:

  1. Part of the software, where are components you need to modify are in one small folder.
  2. Part of the software, where are components are scattered across different parts of the source code.

I think the answer here is pretty obvious, but I’ll leave it to you.

Co-location

The idea here is code co-location basically. It has been written about before.

If you have a system where code related to the same behaviour is scattered - try to bring it closer together - it will make it easier to understand and modify. This in order will increase your productivity, reduce frustrations and improve your joy of work.

The goal is that if you need to understand or modify some behaviour in the system, you will find most, if not all of the functionality easily, in single place and will be able to understand them quickly.

Example: Vue.js

Vue is one of the most popular frontend libraries for creating Single Page Applications. It has something called single file components, which bring together styles, javascript and templates. Let’s take a look at one:

<script>
export default {
  data() {
    return {
      show: true,
      list: [1, 2, 3]
    }
  }
}
</script>

<template>
  <button @click="show = !show">Toggle List</button>
  <button @click="list.push(list.length + 1)">Push Number</button>
  <button @click="list.pop()">Pop Number</button>
  <button @click="list.reverse()">Reverse List</button>

  <ul v-if="show && list.length">
    <li v-for="item of list">{{ item }}</li>
  </ul>
  <p v-else-if="list.length">List is not empty, but hidden.</p>
  <p v-else>List is empty.</p>
</template>

<style>
  ul {
    list-style-type: none;
    padding: 0;
  }
</style>
Enter fullscreen mode Exit fullscreen mode

Now - you have everything to understand this particular component in a single place. It doesn't mean that placing styles, templates and code is the only way to do LoB in SPAs. With React people usually place these in separate files, but in a single folder named after a component.

Example: HTMX

If I’m not mistaken the term “Locality of Behaviour” was coined by creator of HTMX, Carson Gross. In HTMX it refers to the fact that it allows you to embed behaviour in HTML, in a clean and easy to understand (once you understand HTMX) way. It’s not a hammer that you can use to deal with everything, but it makes a large amount of interactions easy to do without any fancy frameworks or writing specialized JS code.

Here’s an example of a “click to edit” functionality:

<div hx-target="this" hx-swap="outerHTML">
    <div><label>First Name</label>: Joe</div>
    <div><label>Last Name</label>: Blow</div>
    <div><label>Email</label>: joe@blow.com</div>
        <!-- when button gets clicked - we'll load edit form and swap it with the outer div -->
    <button hx-get="/contact/1/edit" class="btn btn-primary">
    Click To Edit
    </button>
</div>
Enter fullscreen mode Exit fullscreen mode

See how all of the behaviour is local, and we don’t have to look through files for document.querySelector('.some-magic-class').addEventListener(...)?

See more examples with interactive demos here.

Not just frontend

LoB/co-location comes from frontend world, but it applies to backend as well.

We not always may keep logic in one file, but we can always place related things together. A structure for a hypothetical Python app could look like this:

.
├── thing_module/
│   ├── new_thing.py
│   │   ├── NewThingForm
│   │   ├── NewThingView
│   ├── templates/
│   │   └── new_thing.html
|   └── scss/
|   |   └── new_thing.scss
├── another_module/

Enter fullscreen mode Exit fullscreen mode

Keeping things all together makes things easy to find and modify.

Tensions and interactions

For some reason in software development we tend to present techniques (which some call “principles”) as absolute - as if they stand on their own and have no interactions whatsoever.

Well, things don’t work that way. So let’s take a look at how it interacts with different techniques and aspects of software development.

Separation of Concerns (SoC)

Separation of Concerns is an old technique where we separate computer system into distinct sections, where each addresses separate concerns. Most of the time this means that we have separate “layers”. Some of examples include:

  • IP Stack which is layered, uses different protocols at different layers, each taking care of a separate concern
  • HTML (or templates), CSS and JavaScript in Web development dealing with different aspects of displaying a web page or an application
  • Models, views and controllers in MVC paradigm when working with many backend frameworks

Now, when React first came out - there was a huge pushback from the community because it violates the Separation of Concerns. We started putting together HTML (templates or views) and JavaScript code (controllers).

Not only that - after React got more popular - we started to place CSS in components as well (CSS-in-JS like this):

import styled from 'styled-components';
// Create a component that renders a <p> element with blue text
const BlueText = styled.p`
  color: blue;
`;

<BlueText>My blue text</BlueText>
Enter fullscreen mode Exit fullscreen mode

Now, there is some tension between the two. But mostly only if you treat everything as a principle, not a technique. Thinking of it as a principle means that it’s non-negotiable and it’s always right to follow it, while technique is something you apply where it makes sense and improves things.

The recipe for success here is rather simple:

  • when your components / code is pretty simple - it’s usually better to be able to see everything at once
  • but as the code - for a particular component / code unit grows more complex, at some point it is very useful to be able to understand business logic in isolation, without it being tangled with other concerns like templating or styling
  • so start with something simple and when it grows more hard to understand, start separating those concerns in a way that make it easier to understand
  • but when you separate these concerns to separate files - keep them close together so it’s still easy to understand whole thing - by looking at a single, small folder instead of now having to jump around 3-10 different places

The key here is using the practice that makes sense in given case, not religiously doing the same thing everywhere.

High Cohesion & Low Coupling

These two concepts are key to creating maintainable codebases. Most of so called principles are just reiterations and reinventions of these (and few more) concepts.

  • Cohesion is basically a measure of how code put in the same code unit is related.
  • Coupling is a measure of “how much other code will I have to change if I change X”.

The idea here is that:

  • if we put things that belong together (high cohesion) in a code unit / module
  • and then ensure that changes inside of that module have low impact on the outside of a module (low coupling)
  • then our code will be:
    • easy to understand (you just look at a single place)
    • and easy to modify (modifications of a module do not trigger cascade of changes)

Locality of Behaviour means that you put things which work together - together. Behaviour of a given piece of software is nicely enclosed in a single place, making it cohesive. It works to increase cohesion of your system, and by extension helps with low coupling.

DRY and cross-cutting concerns

Here’s the thing: repeating yourself can sometimes be beneficial. But it doesn’t mean that you can let repetition run rampant in your system, otherwise you’ll end up with incohesive, unmaintainable ball of mud.

I’m not sure where the idea that LoB somehow collides with DRY comes from, but I have two guesses:

  1. Too much of DRY. Basically if someone obsesses over DRY-ing one-liners.
  2. Unaddressed cross-cutting concerns.

The first point is pretty simple. People sometimes obsess over DRY too much, trying to remove each small repetition, usually falling into the traps of false abstractions.

The other one is unaddressed cross-cutting concerns. Think of things like authentication, where instead of calling ensure_has_permission(X) you manually load user and check permissions.

LoB doesn’t mean that you can understand whole piece of software without any prior knowledge. You need to learn cross-cutting concerns and how they work in the system. You need to learn the framework you are working with. In case of HTMX you need to understand HTMX, HTML and general web development to some extent before you can claim that “I can understand this piece by just looking at it”.

Final thoughts

While the term was coined in HTMX environment, which is frontend - LoB is a great way to think about any codebase. Asking yourself questions like:

how much places do I have to go to and check in order to understand this piece?

is a good way of better understanding maintainability of your code.

Top comments (0)