Modular Hyperapp - Part 1
Tags: hyperapp, javascript
Published: 2020-08-17 12:31:10

> ##### _NOTE: this article series is superceded by [this article](https://dev.to/zaceno/a-fresh-take-on-modular-hyperapp-4njm)_

When a codebase grows larger, splitting it into manageable chunks becomes important to keep up momentum. You want bite-sized pieces of app that you can edit and test independently, _without impacting the rest of your code_. 

Some frameworks are built entirely around this principle. In React and Vue you code the various "parts" of your app in the form of "components". [Hyperapp](https://github.com/jorgebucaran/hyperapp) does not have any such constructs to guide you – how you structure your app is entirely up to you.

That can be bewildering, but it is also one of the strengths of such a minimalistic framework. General problems are solved using general programming techniques – not framework specific helpers. Experienced programmers can rely on what they already know, while the novice can be confident what they learn will be useful in future projects and domains.

Nevertheless, it can be bewildering. In this series of articles, I'll present several plain javascript techniques you can use to structure your Hyperapp apps in a _modular_ way.  

I'll kick off the series with a closer look at modules and modularity. It will be fairly high-level and not Hyperapp-specific, but it forms the foundation for the concrete patterns we'll look at in future installments. 

### Modules

Simply put, a module is something that you can easily "plug in" to add some new feature. Think of the International Space Station. When it needs more room to house more astronauts, a living-space module is built _on earth_ and launched in to orbit. In space, all they need to do is plug it in to an available port, and voilá – the space station can now sustain more astronauts. Over time the ISS has handled wear and changing use cases simply by adding and removing modules.

![ISS animation](https://www.nasa.gov/sites/default/files/thumbnails/image/tv-nrt.gif)

In programming, what we typically mean by a module is a code-file which _exports_ some values, functions, classes et c. Other modules can _import_ these things to gain new powers. 

> Much of our discussion about modules and modularity applies to classes and object-oriented programming as well. But since Hyperapp is rooted in the functional programming paradigm, we'll stick to that.

To be able to use modules in javascript, add the type "module" to the script tag which starts your app. Then it can import whatever it needs from other modules.

```html
<html>
  <head>
    <script type="module">
        import {h, text, app} from 'https://unpkg.com/hyperapp'
        import {foo, bar} from './foo.js'
        /*
          ...
          do stuff using, h, text, app
          as well as foo and bar
          ...
        */
    </script>
    ...
```

> For a complete reference on the syntax of exporting/importing from modules, see [this MDN guide](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) 

### Complexity

While space-engineers use modules to avoid the dangers and difficulties of monkey-patching live systems in space, programmers use modules to _manage complexity_.

Once your app gets big enough, looking at your own code can start to feel like looking at an unintelligible tangle of wires. When even the people who wrote it can't understand it, further development is effectively halted. Modules can help avert this fate by breaking the complexity up into smaller chunks, where each module _on it's own_ is manageable.

```js
//This is foo.js

// some reasonably complex stuff:
const zip = ...
const zap = ...
const zorp = ... 

//...hidden behind this simpler interface:
const foo = ... // uses zip, zap & zorp
const bar = ... // uses zip, zap & zorp

export {foo, bar}
```

Every module carries its own _scope_, meaning you can freely assign variables within the module without fear of naming conflicts in other modules. This is an essential feature of modules: they don't know anything about each other besides what they export.

The exports constitute an interface for other modules. As long as you fulfill this contract, it doesn't matter how you do it. Like an ISS module, as long as the docking-port has the right size and shape, with the right connectors in the right places, you can build the rest of the module however you like. 

### The Crux

Just hiding some complicated code behind an interface is not necessarily enough to actually manage complexity. Take this example:

```js
const zip = (zipthing, index) => ...
const zap = (zapthing, value) => ...
const zorp = (zorpthing, options) => ...

const foo = (app) => {
    let zapthing = zip(app.theZipper, app.current) 
    let zapResult = zap(zapthing, app.settings.zapopts.value)
    return zorp(app.theZipper.zorp, {
        ...app.zorpopts,
        zap: zapResult,
    })
}
export {foo}
```

Notice how sensitive it is to the contents of `app`! The idea behind a module like this, was probably to move the logic out of the way to simplify logic elsewhere in the app. But any change to `app` risks breaking the module, so nothing was actually untangled. Instead, parts of the tangle were just hidden away, which only makes matters worse.

Any assumptions about the rest of your app that a module relies on, is technically a _part of the interface_. If the interface is broad, diffuse and sensitive then you aren't really creating the separation between complexities that would help you deal with them.

This is the trick to using modularity to its full advantage: keeping the interfaces _small, simple and robust_. Ideally they should be similar between modules as well. This means implementing your modules with _as few assumptions as possible on the external_. 

Keeping interfaces small and simple is commonly referred to as "loose coupling", and making as few assumptions as possible is known as "the principle of least knowledge" or [the "Law of Demeter"](https://en.wikipedia.org/wiki/Law_of_Demeter)


### Conclusion, Part 1

In summary there is more to modularity than just `export` and `import`. Knowing _when_ it is time to break something out into a module, _what_ to put in that module, and _how_ to design the interface is a subtle art. Like anything, experience is the best teacher. 

In the following installments we'll explore some specific cases and concrete patterns with discussions of their pros and cons.