How spaghetti code comes back - solving the wrong class of problems
Tags: cleancode, frontend, javascript
Published: 2020-05-10 17:11:45

In my previous article I was talking about why [simplicity matters](https://dev.to/latobibor/how-spaghetti-code-finds-it-way-back-to-our-codebase-intro-lme) and in particular how a great language/tech can be the wrong one for the problem at hand.

This article is about the classes of problems and how they can lead to _spaghetti code_.

## Working against coding tools: _"Help us so we can help you"_
In my opinion every principle, pattern and tool is part of `clean code` if they increase your confidence and safety making changes to a codebase. The guides, techniques, trips and tricks aim at "lowering the cognitive load" so you can spend the freed mental space to laser focus on the problem at hand. In other words, `clean code` shifts time spent on _piecing together what the other dev_ did to _figuring out what should be done_.

Following this trail the _tooling around code_ also save time and mental energy, by 
- **catching broken relationships**: _syntactical and semantic checks_ at compilation time or in your `IDE`; e.g. if you have made a typo while accessing a property
- **remembering all requirements**: well defined _unit tests_
- **guarding the flow of data**: like type-checks when you pass parameters (in case of `TypeScript`)
- **keeping track of _all_ references** to a piece of code (in most `IDE`s called `Finding usages`) 
- **code analysis**: from quantitative, performance analysis to linting
- **code optimization**: a well equipped compiler might outperform even a senior developer on code optimization, given you are not standing in the way of it.

> You can unlock all these benefits if your **code is built with these tools in mind**. 

As a side effect they will _increase your productivity_ by decreasing the time needed to _cross-reference code_, i.e. opening a lot of files to check the implementation details.

Now let's see an example where good intention to have additional guarantees leads to breaking many of the tools above.

### Immutability vs. `JavaScript` `object`s
If you have ever worked with `Redux` you might have encountered the problem of the lack of immutable compound structures in `JavaScript`.

If you are unfamiliar with this problem I suggest reading [Why Redux need reducers to be “pure functions”](https://www.freecodecamp.org/news/why-redux-needs-reducers-to-be-pure-functions-d438c58ae468/).


Let's just freshen this up with a super short code example:

```javascript
const nestedValue = 'nested value';
const state = { stringStuff: 'stuff', deepStuff: { nestedValue } };

// let's try to make it immutable
Object.freeze(state); // note: this just mutated your `const` object!
state.stringStuff = 'Cannot do this'; // ERROR - in strict mode, ignored otherwise
state.deepStuff = {}; // ERROR again, can't set a new object reference

// seems we are done, but let's investigate the object referenced by `deepStuff`
state.deepStuff.nestedValue = 'But you can this'; // no error - hmm
state.deepStuff.nestedValue === nestedValue; // FALSE - OMG, what have I done
```

One can argue that it is possible to recursively `freeze` every nested object; but since the `plain old object` of `JavaScript` is super flexible you will have edge cases, like objects holding circular references 😐.

**What's the moral of the story?** `JavaScript` was _not_ designed with _immutability_ in mind. It was also not designed with _object-oriented programming_ in mind and nor with _functional programming_.

If we want them we need some extra help. Enter `immutable.js`.

### Getting immutable nested objects while losing something else
Let's check adapt an example straight from their documentation:

```javascript
import { Map } from 'immutable';

const nestedValue = 'nested stuff';
const state = Map({ stringStuff: 'stuff', deepStuff: Map({ nestedValue }) });
const newState = state.setIn(['deepStuff', 'nestedValue'], 'immutable yay');

// the lib guarantees this way that we did not change `state`
state.getIn(['deepStuff', 'nestedValue'] !== newState.getIn(['deepStuff', 'nestedValue']); 
// TRUE - no more headaches, or...
```

We now have guaranteed immutability. But we replaced the meaningful `object binding`s with `string literal`s. We had a headache because of possible mutations and now we have a refactoring nightmare as we now our object's API! 😐 

We clearly broke our `object bindings` by **[stringly typing](https://devcards.io/stringly-typed)** them!

> We obfuscated the relationship between the object property `state.deepStuff` and the string `'deepStuff'`! We `literal`ly turned the help off of most of our tools!

Since `string literals` are simple values they can be _anything_! Whenever you deal with strings remember Let's see these examples:

```javascript
// no errors in any of these cases:
// Did you find the typos? Your code reviewer might also miss them!
state2 = state.setIn(['deepSutff', 'netsedValue'], 1); 

// string literals can be anything, like your friend's phone number or a date!
state2 = state.setIn(['+36 (12) 3456756', '2020-05-09'], 1); 

// they can be really 'fuzzy' (see also: 'fuzz testing')
state2 = state.setIn(['😐|{}_+]`', '開門八極拳'], 1); 
```

So to recap: we reached the zen of `immutability` but we **broke most of our tooling**, so now we...
- have **no code completion** => prone for typos
- have only **runtime errors**
- need to do **full text search** to see who is depending on our structure (good luck finding `deepSutff` by searching for `deepStuff`)
- have to be extra careful with refactors, since no tool will warn us about **broken references**

> In short we **replaced the problem** of mutability with the problem of brittle code.

## Mitigating the `wrong problem class` issue
Before _enforcing a pattern_ on your codebase make sure you understand the trade-offs it brings, and then think about the possible _frequency_ and _severity_ of the problems solved and caused by said pattern. 

> Theoretical benefits must be weighed against the _tooling_ you have and the skills of your development team.

In my example above, I'm pretty sure _accidental mutations of objects_ happen less frequently than _renaming_ or _looking up objects_ and their properties. So a codebase which does not require the special features of `immutable.js` might be better off without it. Luckily in this particular there are alternatives which do not break `object binding`: check out [immer.js](https://github.com/immerjs/immer).

> But if you don't have an alternative you can still _box the ugliness_ by utilizing it in only a handful of _mission critical cases_.

That way you can also create _wrappers_ around it, so it is easy to replace the implementation in a later time when the better alternative already surfaced.

### Remarks about `stringly typed API`s
If you have any influence over a future library then please **never design an API that depends on `string literals` for meaningful business**. Remember, **`string literal`s are values** that _should not point to objects_ but should be used for labels in _user interfaces_, _paths_ for files or _data_ stored in databases.


#### Bonus: my favorite Angular 1 tooltip fail
This is how I lost an entire working day on the anti-pattern combination of `stringly typed` and `swallow the error message`. (Sorry, this is gonna be an HTML example, not a purely `JavaScript` one). Product wanted a little tooltip to appear over a `<button />` on `mouseenter` events. I was using `angular-uib` library to implement it and it did not want to work - also it did not output any errors.

```html
<!-- This does not work, NO ERRORS  -->
<button 
  uib-popover="Hello world!" 
  popover-trigger="mouseenter">
  Mouseenter
</button>

<!-- This works -->
<button 
  uib-popover="Hello world!" 
  popover-trigger="'mouseenter'">
  Mouseenter
</button>
```
Did you see the problem? No? I have tried `mouse-enter`, `mouseEnter` and everything in between. 

> The right way was to put the `mouseenter` into single quotes inside the double quotes, as it was meant to be a `string`. 😐

#### Thanks for reading this article!
And if you have any comments, especially if you want to improve the grammar of this post let me know; I am not a native English speaker, so I am super grateful for any stylistic suggestions!