Funções Puras e imutabilidade, um pouco sobre código limpo e qualidade
Tags: javascript, codequality, functional, ptbr
Published: 2019-09-16 20:28:37

Código limpo (_clean code_) é um tópico recorrente e importante. Infelizmente não há uma adoção tão grande quanto se imagina ou espera.

Entre as motivações estão a falta de aprofundamento do tópico e suas nuances, bem como uma boa contextualização e a realidade dos projetos ou empresas onde os profissionais de desenvolvimento de software trabalham.
O caos, a cobrança e demanda, muitos líderes ou gerentes não conseguem, ou simplesmente não veem valor nessas práticas, o bom e velho _“o importante é funcionar”_.

Talvez algumas pessoas fiquem chocadas ou surpresas ao saber que ainda há muitas empresas e profissionais não sigam práticas e conceitos consideradas por muitos básicas.

Este artigo visa ajudar a desmistificar alguns tópicos e técnicas, facilitando a adoção das mesmas.

---

## Clean Code e JavaScript

Clean Code é um assunto extenso, até mesmo controverso em alguns aspectos. Algumas coisas podem ser consideradas subjetivas, debates não são raros.

Infelizmente muitos desses debates são [bikeshedding](https://pt.wikipedia.org/wiki/Lei_da_trivialidade), resultam em nada. Quando isto acontece, quem esta na posição de liderança do time precisa tomar uma postura rígida e clara, algumas vezes unilaterais. Não é algo simples, afinal pode gerar conflitos no time, porém, é algo necessário.

Meu conselho é: seja honesto e claro, a intenção não é impor uma vontade, mas sim encerrar diálogos improdutivos.

### Ferramental

Muitas dessas discussões são sobre ponto e vírgula… Tabs ou espaços… [CamelCase](https://pt.wikipedia.org/wiki/CamelCase) ou [Snake Case](https://en.wikipedia.org/wiki/Snake_case)… A solução é simples, adotar um estilo pré existente e consolidado e seguir com ele sem nenhuma alteração.

No JavaScript possuímos alguns padrões já bastante populares: [Google Style Guide](https://google.github.io/styleguide/jsguide.html), [Airbnb Style Guide](https://github.com/airbnb/javascript) e [JavaScript Standard Style](https://standardjs.com/). Todos possuem suas regras para o [ESLint](https://eslint.org/). Escolha um deles e adote para seu time ou projeto, recomendo não fazer modificações nas regras.

Há plataformas que vão além do que o ESLint faz, analisando e classificando seu código. Algumas são gratuitas para projetos open-source.

- [DeepScan](https://deepscan.io/home/)
- [SonarJS](https://www.sonarsource.com/products/codeanalyzers/sonarjs.html)
- [Prepack](https://prepack.io/)
- [CodeFactor](https://www.codefactor.io/)
- [Code Climate](https://codeclimate.com/)
- [Codacy](https://www.codacy.com/)
- [Codebeat](https://codebeat.co/)
- [SonarQube](https://www.sonarqube.org/)

### Técnicas

São várias, este artigo vai se focar em [**Imutabilidade**](https://www.sitepoint.com/immutability-javascript/) e [**Funções Puras**](https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-pure-function-d1c076bec976). Porém, não podemos deixar de citar algumas como [DRY](https://pt.wikipedia.org/wiki/Don%27t_repeat_yourself), [SOLID](https://pt.wikipedia.org/wiki/SOLID), [KISS](https://pt.wikipedia.org/wiki/Keep_It_Simple) e [Object Calisthenics](https://eltonminetto.net/2016/06/24/como-melhorar-seus-codigos-usando-object-calisthenics/).

Ao se aprofundar nessas técnicas você perceberá a convergência de ideias e fluxos, com o tempo e prática elas se tornaram naturais ao seu estilo de codificação.

---

## Imutabilidade

Imutabilidade talvez seja um dos tópicos mais complexos deste artigo. Não uma complexidade técnica, mas uma complexidade quase filosófica e de alguma _falsos positivos_.

> A primeira coisa que devemos entender, imutabilidade não significa constante.

```js
const a = 73
a = 37
```

![js const error](https://thepracticaldev.s3.amazonaws.com/i/5eme2dfcibd021zxqiwe.png)

Este é o erro que recebemos ao executar o exemplo anterior, muitos vão assumir que [**_const_**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const) no JavaScript esta relacionado a constante, porém seu objetivo é impedir a **reatribuição** de uma variável.

Vamos entender um pouco mais como isso funciona.

## Valores e Referências

Em JavaScript temos os seguintes primitivos:

- [Boolean](https://developer.mozilla.org/pt-BR/docs/Glossario/Booleano)
- [Number](https://developer.mozilla.org/pt-BR/docs/Glossario/N%C3%BAmero)
- [String](https://developer.mozilla.org/pt-BR/docs/Glossario/String)
- [Null](https://developer.mozilla.org/pt-BR/docs/Glossario/Nulo)
- [Undefined](https://developer.mozilla.org/pt-BR/docs/Glossario/Undefined)
- [Symbol](https://developer.mozilla.org/en-US/docs/Glossary/Symbol)

Quando usamos os operadores de comparação `==` e `===` internamente e em resumo, estamos comparando referências de memória. JavaScript é uma linguagem 100% orientada a objetos, todos os seus tipos e valores são objetos.

Objetos vão possui um endereço de memória, na prática `x == y` esta comparando se o endereço de memória que `x` representa, é o mesmo que `y` representa. Por isso que `{} == {}` ou `[] == []` não funciona, pois cada `{}` e `[]` apontam para locais diferentes da memória.


Sabendo que `2` é do tipo Number e também um objeto, como `2 == 2` funciona? A resposta é simples, os tipos primitivos em JavaScript são constantes e imutáveis.

```js
01. let n = 73
02. n == 73 // true
03. n = 37
04. n == 73 // false
```

- Na linha `01.` declaramos `n` e atribuímos `73` para ele.
- Na linha `02.` comparamos `n == 73`, como `n` aponta para o mesmo endereço de memória que `73` temos `true` como resultado.
- Na linha `03.` reatribuímos `n` a outro valor, agora `37`.  
- Na linha `04.` comparamos `n == 73`, como `n` aponta para outro endereço e não para `73`, temos `false` como resultado.

Todo primitivo em JavaScript se comporta dessa maneira, outra característica relacionada a este comportamento é a imutabilidade deles, métodos de um primitivo não modificam seu valor… não alteram sua referência na memória, quando necessário geram novas referências, mas nunca modificam a original.

```js
const s1 = 'Codecasts'
const s2 = s1.toUpperCase() // CODECASTS
s1 == s2 // false
```

Este comportamento é nativo dos primitivos, porém outros tipos como `Array` e `Object` não se comportam dessa maneira, `[7, 3] == [7, 3]` sempre será `false`, pois cada um aponta para um endereço de memória diferente.

> [Explaining Value vs. Reference in Javascript](https://medium.com/p/explaining-value-vs-reference-in-javascript-647a975e12a0)

## Referências Imutáveis

Quando estamos falando de imutabilidade em JavaScript estamos falando em *evitar modificar referências*.

Ao usar `const` em primitivos já estamos garantindo uma boa parcela de imutabilidade, porém o maior desafio é trabalhar com objetos e listas imutáveis.

```js
01. const arr = [7, 3]
02. arr.push(12)           // arr --> [7, 3, 12]
03. arr.unshift(1)         // arr --> [1, 7, 3, 12]
04. const last = arr.pop() // arr --> [1, 7, 3]
05. last == 12 // true
```

> Refs: [`.push`](https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Array/push), [`.unshift`](https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Array/unshift) e [`.pop`](https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Array/pop)

Na linha `01.` declaramos `arr`, atribuímos nele um array `[7, 3]`. Nas linhas `02.` a `04.` manipulamos `arr` e em seguida é ilustrado seu estado atual.

Inicialmente coisas assim podem parecer inofensivas, mas há um potencial de cenários extremamente difíceis de serem identificados ou **rastreados**.

O próximo exemplo ilustra como objetos são usados como referência.


```js
const fn = x => x
const obj = {}
obj == fn(obj) // true
```

A função `fn` não faz nada além de retornar `x` seu único argumento. Ao comparar `obj == fn(obj)` temos `true` como resultado, pois estamos retornando a mesma instância/referência.

O exemplo abaixo explora brevemente os impactos desse comportamento.

```js
const setPrice = product => {
  product.price = 73
}
const product = { name: 'Instant Kung Foo.' }
setPrice(product)
product.price == 73 // true
```

A função `setPrice` recebe `product` e adiciona ou modifica a propriedade `price`, isso fica claro quando comparamos `product.price == 73`. Isso se chama **efeito colateral (_side effect_)**, a função `setPrice` é uma **função impura**.

```js
const setPrice = product => {
  return { ...product, price: 73 }
}
const product = { name: 'Instant Kung Foo.' }
const newProduct = setPrice(product)
product.price == 73 // false
newProduct.price == 73 // true
```

Agora a função `setPrice` retorna uma [**cópia rasa** ](https://we-are.bookmyshow.com/understanding-deep-and-shallow-copy-in-javascript-13438bad941c)**(_shallow copy_)** de `product` que recebeu como argumento, acrescentando ou modificando a propriedade `price`. Esse procedimento obriga a criação de uma **nova variável** `newProduct`, isso acontece pois `const` foi utilizado, o próximo exemplo usa `let`.

Este fluxo pode incomodar um pouco, normalmente esta abordagem não é necessária, usa-se técnicas como [pipe e compose](https://medium.com/front-end-weekly/pipe-and-compose-in-javascript-5b04004ac937).

## Funções Puras

[Funções puras (pure functions)](https://medium.com/@jamesjefferyuk/javascript-what-are-pure-functions-4d4d5392d49c) andam ao lado da imutabilidade, na prática podemos dizer que uma não existe sem a outra.

> Entende-se como pura a função que sempre devolve o mesmo resultado para um mesmo argumento, não importando a quantidade de vezes que ela é chamada, sem causar efeitos coletareis (side effects).

Parece e é obvio, porém, infelizmente ainda causa dúvidas para alguns programadores. Essa técnica exige um pouco de esforço por parte do programador, ele tem que explicitamente optar por usa-la.
Vajamos o seguinte exemplo:

```js
// impure.js
let counter = 0
const addToCounter =  value => {
  counter = counter + value
}
// pure.js
const sum = (x, y)  => x + y
let counter = 0
counter = sum(counter, 10)
```

Este é um dos primeiros exemplos usados para descrever uma função impura, é bem óbvio. Porém sua simplicidade também dificulta sua adoção, sabemos que um programa real é mais complexo do que essa função.

```js
const request = params => {
  if (params.client) {
    params.client_id = params.client.id
    delete params.client
  }
  return http.get('/', params)
}
```

O exemplo anterior é mais próximo a uma aplicação _real_. Temos a função `request` que recebe `params`, caso `params.client` exista, `params` é modificado.

A função `request` gera um efeito colateral gravíssimo, é impossível determinar a instabilidade e consequentemente _bugs_ que essa função causa para quem a chamar.

```js
const request = params => {
  const options = { ...params }
  
  if (options.client) {
    options.client_id = options.client.id
    delete options.client
  }
  return http.get('/', options)
}
```

Com esta mudança o problema foi resolvido, `params` não é mais modificado, o procedimento agora é aplicado a uma cópia dele. Porém, ainda há como melhorar esta função.


```js
const parseRequestParams = params => {
  const data = { ...params }
  if (data.client) {
    data.client_id = data.client.id
    delete data.client
  }
  return data
}
const request = params => {
  const options = parseRequestParams(params)
  return http.get('/', options)
}
```

Toda a lógica de tratamento dos parâmetros foi isolada em `parseRequestParams`. A função passa a ser mais objetiva, delegando parte da lógica a outra função, uma função mais especializada. Isso não apenas melhora a leitura como permite criar testes de unidade com maior cobertura de possibilidades.

> Uma linha, um comando, uma função.

A função `parseRequestParams` ainda pode evoluir ao utilizar técnicas com **_pipe_** e **_compose_**. A biblioteca [**ramda**](https://ramdajs.com/) fornece essas e várias funções úteis para ajudar na implementação dessas técnicas.

```js
import { compose, omit } from 'ramda'

const parseClientInRequestParams = params => {
  if (!params.client) {
    return params
  }

  return {
    ...omit(['client'], params),
    client_id: params.client.id
  }
}

const parseUserInRequestParams = params => {
  if (!params.user) {
    return params
  }

  return {
    ...omit(['user'], params),
    user_id: params.user.id
  }
}

const parseRequestParams = compose(parseClientInRequestParams, parseUserInRequestParams)
```

As funções `parseClientInRequestParams` e `parseUserInRequestParams` ficaram muito similares, podemos simplifica-las.

```js
import { compose, omit, has, path } from 'ramda'
const createParseEntityInRequestParams = key => {
  return params => {
    if (!has(key, params)) {
      return params
    }
    const id = path([key, 'id'], params)
    return {
      ...omit([key], params),
      [`${key}_id`]: id
    }
  }
}
const parseClientInRequestParams = createParseEntityInRequestParams('client')

const parseUserInRequestParams = createParseEntityInRequestParams('user')
```

A função `createParseEntityinRequestParams` retorna uma função que funciona exatamente como as implementações anteriores de `parseClientInRequestParams` e `parseUserInRequestParams`.

> Receber e retornar funções só é possível pois funções são [Cidadãos de Primeira-Classe (First-class citizens)](https://en.wikipedia.org/wiki/First-class_function) no JavaScript, são tratadas como valores.

Com o tempo mais padrões desses vão surgindo e se torna natural o uso de funções puras e [factory functions](https://medium.com/javascript-scene/javascript-factory-functions-with-es6-4d224591a8b1). É um nível de reuso muito grande e de extrema flexibilidade.

### Encapsulando Fluxos Impuros

Infelizmente não é viável criar uma aplicação que não lide com fluxos impuros. Operações que dependem de _i/o_, _disco_, _rede_, _banco de dados_… São operações com impuras e muitas vezes vão produzir efeitos colaterais.

Quando falamos de programação funcional, não é raro termos como [_monads_](https://en.wikipedia.org/wiki/Monad_(functional_programming)) aparecerem. Eles dão origem a muita confusão, em especial para quem ainda esta apenas começando com programação funcional.

Este artigo não vai tratar de _monads_, e sim de uma abordagem simplista para essas situações.

```js
const request = params => {
  const options = parseRequestParams(params)
  return http.get('/', options)
}
```

Reaproveitando o exemplo anterior, temos a função `request`. Por mais que ela não gera mais um efeito colateral direto ela retorna uma [promessa](https://developers.google.com/web/fundamentals/primers/promises?hl=pt-br). Existe uma discussão sobre a “pureza” de uma função que retorna uma promessa, a verdade é que ela poderá ter [resultados diferentes](https://medium.com/@aidobreen/js-promises-async-await-and-functional-programming-f2e5fa66b4ef) mesmo que os argumentos sejam os mesmos, isso torna ela uma [**função impura**](https://medium.com/@aidobreen/js-promises-async-await-and-functional-programming-f2e5fa66b4ef).

Sendo ela uma função pura ou não, há um problema mais “grave”, o fato dessa função depender de `http` para funcionar. Assumimos que `http` é uma global, provavelmente uma instância do [axios](https://github.com/axios/axios). Justamente por ser global que temos um problema na hora de testar esta função.

Depender de estados globais prejudica muito o processo de testes, pois é necessário recriar toda uma camada de ambiente para que o teste seja possível, além de usar técnicas sofisticadas de [mocking](https://sinonjs.org/).

A solução é simples, injetar `http` em `request`.

```js
const request = (http, params) => {
  const options = parseRequestParams(params)
  return http.get('/', options)
}
```

Isso pode ser particularmente verboso na hora de utilizar, porém com técnicas de injeção de dependências isso passa a ser simples e prático. Os links abaixo demonstram algumas técnicas que vão ajudar.

- [Container de Injeção de Dependências com JavaScript](https://blog.codecasts.com.br/dependency-injection-container-javascript-49e9eb2c399c)
- [Injeção de Serviços em Middlewares HTTP no Node.js](https://blog.codecasts.com.br/dependency-injection-em-http-middlewares-node-js-91bed97fc955)

{% youtube sD94szvFqGw %}

---

Este artigo possui um objetivo **subliminar**, introduzir o leitor a programação funcional sem que ele perceba. Todo o conteúdo aqui apresentado faz parte das técnicas e fluxos de programação funcional com JavaScript.

Quando se fala de programação funcional muito se fala de [_lambda_](https://pt.wikipedia.org/wiki/C%C3%A1lculo_lambda), [_functors_](https://pt.wikipedia.org/wiki/Functor), [_monads_](https://www.youtube.com/watch?v=cB0vpg9-YMQ) e outros termos… Programação funcional possui diversas camadas e convive bem com outros paradigmas.

Seguir os princípios da programação funcional é uma das melhores maneiras de se obter um código limpo e de qualidade.

Em minha concepção pessoal um código de qualidade é um código testável. Talvez a maior dificuldade em se implementar testes seja justamente escrever código testável. Imutabilidade e funções puras são os primeiros passos.

{% youtube bVfHSc38dLU %}

---

Este artigo foi originalmente postado em [2018-12-30, no Medium](https://blog.codecasts.com.br/pure-finctions-immutability-clean-code-quality-31825b0d7516)

---

Se quiser saber mais sobre meu trabalho visite [**dev.to/codecasts**](https://dev.to/codecasts) ou  [**blog.codecasts.com.br**](https://blog.codecasts.com.br/). Assine nosso [canal no YouTube](https://www.youtube.com/channel/UCTluPqMkm90zyw6mCde561A), lá você vai ver vídeos sobre [JavaScript](https://www.youtube.com/watch?v=fBInMy61plk&t=0s&list=PLy5T05I_eQYNQs4Pta85XRSucm3IOHx2M&index=2), [jQuery](https://www.youtube.com/playlist?index=1&list=PLy5T05I_eQYN8T15w4KLLcDcjAmIXDyu-&playnext=1), [Gulp](https://www.youtube.com/watch?v=eukZI7Rcrss&t=0s&index=2&list=PLy5T05I_eQYPl_iF2aJ0T0JBgJMatOLwv), [ES6](https://www.youtube.com/watch?v=yRr8Wo4XfYY&list=PLy5T05I_eQYOoUz2TtAqq35RLCc-xBZCe&index=2&t=0s), [Vue.JS](https://www.youtube.com/watch?v=ck8uNY0B0HI&index=2&t=0s&list=PLy5T05I_eQYOr3uPSI-eTVCLe1GF1qOf5) e muito mais. Também não deixe de entrar em contato pelo nosso grupo no [Telegram](https://t.me/codecasters)