Locality of Behavior on its own isn't enough

The LoB principle dictates that the behavior of a unit of code should be as obvious as possible by looking only at that unit of code. But I think something is missing from this discussion.

Locality of Behavior on its own isn't enough

The documentation for htmx refers to something called "Locality of Behavior".

The behaviour of a unit of code should be as obvious as possible by looking only at that unit of code
The LoB principle is a simple prescriptive formulation of the quoted statement from Richard Gabriel. In as much as it is possible, and in balance with other concerns, developers should strive to make the behaviour of a code element obvious on inspection.

The author was talking about how htmx demonstrates this principle well when compared to jQuery. And indeed it does. Most modern view libraries are all great examples of this, to varying degrees. React, Vue, Stimulus, Alpine, htmx, and many others, each have their events, actions, and targets defined in the html to different extents, therefore making it easier to know that something special is happening here.

But, is simply placing these things next to each other enough to make a unit of code "obvious"? I think there's more to the discussion.

🤖
Obvious (ob·​vi·​ous)
Easily perceived or understood; apparent.

There's something missing

The notion of pairing these concerns together, on its own, doesn't guarantee that it is obvious by looking only at that unit code. Each of these libraries comes with the possibility of obscuring what's really going on behind the curtain. We've all seen code like this.

<button hx-get="/clicked">Click Me</button>

htmx example

<form onSubmit={this.handleSubmit}>
    <label>{this.props.prompt}</label>

    <input onChange={this.handleChange} value={this.state.text} />

    <button>Add #{this.state.items.length + 1}</button>
</form>

React.js example

<form data-controller="page">
    <input data-action="input->page#handleInput">
    
    <ul data-page-target="container"></ul>
</form>

Stimulus.js example

...and so on. These examples could be doing anything. They are all doing something, of that we can be sure. And while this a step in the right direction, still far from what many would consider to be obvious.

🤣
It's much easier to get away with this when you have the rest of the code in the same file like you do with React and Vue's Single-File Components. Scrolling's not that hard.

Naming things is hard

These libraries all provide a way to declare what is happening, when it occurs, and where the underlying logic can be found. All you, as the author, have to do is to name things well enough so that the reader might be given some insight without going source diving.

This is a minor gripe. Too many of these libraries include examples on their front page like the ones above that don't convey anything about anything. Sure, it's just an example. I get it. But this is where beginners go to learn.

Stimulus, on the other hand, provides a great example for beginners on their front page. 👏

<div data-controller="hello">
    <input data-hello-target="name" type="text">

    <button data-action="click->hello#greet">
        Greet
    </button>

    <span data-hello-target="output">
    </span>
</div>

And again and again throughout their documentation.

<form data-controller="search">
    <input data-action="input->search#loadResults">
    
    <ul data-search-target="results"></ul>
</form>

They get it - pairing actions and events with your markup doesn't guarantee transparency, insight, or understanding by itself. For that you need more.

Stimulus isn't the only one who gets it

Take a look at this controller found within Laravel Fortify and take note of how long it takes you to fully wrap your head around what it is responsible for.

class PasswordController extends Controller
{
    public function update(Request $request, UpdatesUserPasswords $updater)
    {
        $updater->update($request->user(), $request->all());

        return app(PasswordUpdateResponse::class);
    }
}

You have to admit that this code satisfies the LoB principle pretty damn well. There's more to see, but you can probably guess what it does. Laravel gets it too.

Conclusion

One can only achieve behavior transparency to a certain extent without also taking a minute to name things well. Hopefully, bringing this missing part of the equation into the discussion will increase the efficacy of the LoB prescription.

The function of good software is to make the complex appear to be simple. ~ Grady Booch