No More 変数汚染

# 概要

長年Ansibleのコードをメンテしていると変数汚染の被害に合うことありませんか？
Ansibleに限らず一般的なプログラミングでも同様な問題が発生します。

変数汚染とは新規作成したタイミングに整合性が保たれている変数が運用していくうちに**うっかり同じような変数名**をつけ、思わぬバグに見舞われたりすること。

基本にして大事なことなので改めてまとめさせて頂きます。
今すぐにできるカイゼンもあるので、是非お試しください。

# その1 命名規則

Ansibleに限らず、通常のプログラミングにおいても命名規則は非常に大事です。
Ansibleには[変数の優先順位](https://docs.ansible.com/ansible/2.9_ja/user_guide/playbooks_variables.html#ansible-variable-precedence)があるので、変数を上書きしながら柔軟に運用することができます。
しかし、その反面変数管理が煩雑になり、事故を招くことがあります。
なので、変数がどこで宣言されたのかをパッと見わかるように命名規則を設けています。
そうすることでどの優先順位の変数を扱っているのかを意識しやすくなり、同じ変数名をつけるリスクも下がる。
例えば、変数が`group_vars/all.yml`で定義されているのか、`set_fact`で定義されたのか、はたまた`register`の変数なのかがわかるようにしています。

- `group_vars/all.yml`の場合･･･トップレベルは`xxx_all`

    ```group_vars/all.yml
    ---
    # 例
    qiita_all:
      item: 12345
    ```
- `set_fact`の場合･･･タスク内で`_xxx`（アンダーバー始まりの変数名）

    ```hoge_task.yml
    # 例
    - ansible.builtin.set_fact:
        _qiita_item: 12345
    ```

- `register`の場合･･･`_result_xxx`

    ```hoge_task.yml
    # 例
    - ansible.builtin.command: "xxx"
      register: _result_item
    ```

# その2 変数のスコープを限定する

先日の「[Ansibleタスクの再利用性を高めるためのタスクの粒度について](https://qiita.com/comefigo/items/f6cdeb1c2ce1ba33c0e7)」のように共通処理をタスク化し、変数で振舞を変えるような機構で実装している場合は[`import_tasks(include_tasks)`に`vars`で変数を引き渡す](https://docs.ansible.com/ansible/2.9_ja/user_guide/playbooks_reuse_includes.html#include)ことで、その変数の認識範囲が`import_tasks`内に留めることができます。
共通処理の振舞を変えるために定義した変数が想定外の場所で悪さすることを防ぐことができます。
ただし、`import_tasks`内でさらに`import_tasks`している場合はそれらにも変数が適用されるのでご注意ください。
ちなみに上記の過去記事の実装では、変数汚染が発生しますので、今となれば自プロジェクト内ではNGな実装になっています。

サンプルコード

```pearents.yml
---
- name: Test 
  hosts: localhost
  connection: local
  tasks:
    - ansible.builtin.import_tasks: ./child1.yml
      vars:
        my_var: "hogehoge"

    # my_varが未定義のため、エラーになる
    - ansible.builtin.debug:
        msg: "{{ my_var }}"
```

```child1.yml
---
- ansible.builtin.debug:
    msg: "子"

- ansible.builtin.debug:
    msg: "{{ my_var }}"

- ansible.builtin.import_tasks: ./grandchild.yml

```

```grandchild.yml
---
- ansible.builtin.debug:
    msg: "孫"

- ansible.builtin.debug:
    msg: "{{ my_var }}"
```

実行結果

```text
bash-4.2# ansible-playbook playbooks/pearents.yml 

PLAY [Test] *******************************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************************************
Sunday 21 November 2021  16:29:36 +0900 (0:00:00.015)       0:00:00.015 ******* 
ok: [localhost]

TASK [ansible.builtin.debug] ***************************************************************************************************************
Sunday 21 November 2021  16:29:36 +0900 (0:00:00.485)       0:00:00.501 ******* 
ok: [localhost] => {
    "msg": "子"
}

TASK [ansible.builtin.debug] ***************************************************************************************************************
Sunday 21 November 2021  16:29:36 +0900 (0:00:00.018)       0:00:00.520 ******* 
ok: [localhost] => {
    "msg": "hogehoge"
}

TASK [ansible.builtin.debug] ***************************************************************************************************************
Sunday 21 November 2021  16:29:36 +0900 (0:00:00.019)       0:00:00.539 ******* 
ok: [localhost] => {
    "msg": "孫"
}

TASK [ansible.builtin.debug] ***************************************************************************************************************
Sunday 21 November 2021  16:29:36 +0900 (0:00:00.018)       0:00:00.558 ******* 
ok: [localhost] => {
    "msg": "hogehoge"
}

TASK [ansible.builtin.debug] ***************************************************************************************************************
Sunday 21 November 2021  16:29:36 +0900 (0:00:00.019)       0:00:00.577 ******* 
fatal: [localhost]: FAILED! => {"msg": "The task includes an option with an undefined variable. 
The error was: 'my_var' is undefined\n\nThe error appears to be in '/ansible/ansible/playbooks/pearents.yml': line 10, 
column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n    - ansible.builtin.debug:\n      ^ here\n"}

PLAY RECAP ***************************************************************************************************************
localhost: ok=5    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   

Sunday 21 November 2021  16:29:36 +0900 (0:00:00.008)       0:00:00.586 ******* 
=============================================================================== 
Gathering Facts --------------------------------------------------------------------------------------------- 0.49s
ansible.builtin.debug --------------------------------------------------------------------------------------- 0.02s
ansible.builtin.debug --------------------------------------------------------------------------------------- 0.02s
ansible.builtin.debug --------------------------------------------------------------------------------------- 0.02s
ansible.builtin.debug --------------------------------------------------------------------------------------- 0.02s
ansible.builtin.debug --------------------------------------------------------------------------------------- 0.01s
```

# その3 変数名

上記二つの方法である程度変数汚染を抑えることができますが、変数名が指し示すものがわかりづらかったり、抽象すぎたりすると効果がなかったりします。
直感的でわかりやすい名前付け、具体的な名前付けを心がけるようにしましょう。
プログラマーのみならず、様々なところで有用なスキルだと思いますので、書籍「[リーダブルコード](https://www.oreilly.co.jp/books/9784873115658/)」を是非一読されることをお勧めします！