Hazy Idea Storage

エンジニアリング全般についての備忘録

問題解決の一歩手前から考える

今年の3月からエンジニアリング組織全体の開発生産性の向上をメインテーマとして業務に取り組んでいる。

半年間やってきて思うのは、思考の出発点が「問題解決」からスタートしないことが非常に重要だということ。

もっと言えば、「解くべき課題はなにか」を設定することにもっと慎重になるべきであり、そこのステップの検討を疎かにしたまま、「とりあえずやってみる」という態度を取ることは望ましくない。

問題解決の手法は今やコモディティ化してきて、ロジックツリーやMECE分解などフレームワークは人口に膾炙してきた感がある。

ただ一方で、「そもそも我々が解こうとしている課題は何であり、その課題設定が本当に正しいんだっけ?」としつこく問うスキルというのは、まだコモディティ化していないように感じる。

生成AIの台頭で我々は気軽にAIに対して「質問をする」ことが出来るようになり、それに対して高度な回答を得られるようになった。

つまり、どんな問を投げかけるかさえ決めることができてしまえば、それなりの精度の答えを得られるようになったわけだ。

でも、AIは我々に対して、「どんな問を立てればよいか」という所まではゼロからサポートしてくれない。

もちろん、とても抽象的な問を立ててchatGPTに質問してそこからドリルダウン的に具体的な問を立てていくというように、抽象から具体へのステップを踏むのには非常に役に立つ。

下記のスクショのように、漠然とした質問からスタートして、問題解決のための思考過程のベストプラクティスをトレースできるのは非常に勉強になる

こうしたコスト問題は、問を作り出す難易度は比較的低い課題だと思う。なぜならコストは低いほうが良いという簡単かつ明瞭な価値判断が所与のものとして存在しており、その問を生み出すこと自体は誰だって簡単にできるからだ。

あとは、その問を解くための問題解決プロセスの全体像をchatGPTに描いてもらい、そこから肉付けを人間がしていけば良い。

難しいのは、「そもそもGCPを使う必要ってあるんだっけ」とか、「インフラアーキテクチャのうち、この部分だけオンプレ運用したほうがいいのでは?」というような問を立てることだと思う。

どうやって既存の枠組みで問題解決をするかというフレームにとらわれず、「そもそも、なんで現状こうなっているんだっけ?」という違和感にふと気づき、その違和感を言語化する力というのは、まだAIは代わりにやってくれないはずだ。

そしてその力こそが、「問題解決」の一歩手前にある、「問題発見・定義」の力なんじゃないか。

そうした力が活きる営みをやっていけたらいいなと思う。

【Rails】gemの中のコードに対してbinding.pryを仕込んでデバッグ

gemの中にbinding.pryを書いても、rails consoleの中でコードを実行するとbinding.pryは反応しない(ブレークポイントで止まってくれない) Rake Taskとして実行することでちゃんと止まってくれるようになるので、以下やり方を備忘録的にメモ

まずはpryするためにGemfileに以下を追加

group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
  gem 'pry-rails'
  gem 'pry-byebug'
  gem 'pry-doc'
end

binding.pryを、ブレークポイントを仕込みたいファイルに記述(例としてsettinglogic.rbを記載しています)

# A simple settings solution using a YAML file. See README for more information.
class Settingslogic < Hash
  class MissingSetting < StandardError; end

...(中略)

      def method_missing(name, *args, &block)
          binding.pry #ブレークポイント
          instance.send(name, *args, &block)
      end

...

lib/task配下にrakeファイルを以下のように作成し、ブレークポイントを設置したコード部分を呼び出すタスクを書く

namespace :main do
  desc "settinglogic"
  task :task => :environment do
    result = Settings.cool
    p result
  end
end

あとは bundle exec rake main: をすれば目当てのブレークポイントで止まる。

$ bundle exec rake main:task

From: /Users/user/development/code_reading/vendor/bundle/ruby/2.6.0/gems/settingslogic-2.0.9/lib/settingslogic.rb:67 Settingslogic.method_missing:

    65: def method_missing(name, *args, &block)
    66:   binding.pry
 => 67:   instance.send(name, *args, &block)
    68: end

おわり

【Docker】Swarmによるでのデプロイ時のハマりメモ

本を読みながらSwarmによるデプロイをしようとしてちょっとハマったので、自分用メモを残しておく。

何をやろうとしたか

  • コマンド docker stackでのデプロイ作業をするため以下のコマンドを叩いた
docker container exec -it manager docker stack deploy -c /stack/todo-mysql.yml todo_mysql
  • エラー文
open /stack/todo-mysql.yml: no such file or directory

そんなファイルなどない、と怒られた。コンテナ-ホストマシン間でのマウントorファイル共有がうまくできていないのだろうか。

原因

推測通り、 docker-compose up で立ち上げていたコンテナ群と、ホストマシンとのファイル共有がうまくできていなかった。 docker-compose.yml の記述に以下を追加し、stackディレクトリを共有するようにした

volumes:
      - "./stack:/stack"

deployまでの手順

# コンテナ群の立ち上げ
docker-compose up -d

# stackディレクトリが共有できているか確認
docker container exec -it manager ls /stack

# コンテナ群(=クラスター)を管理するための司令塔となるmanagerをswarm initで設定。swarmクラスタを生成する
docker container exec -it manager docker swarm init

# manager以外のworkerコンテナをswarmクラスタにjoinさせる
docker container exec -it worker01 docker swarm join --token SWMTKN-1-54n68bqnzn31gldc5c18l554fzk78g9p5xf08jff7781nhr5y4-4qp857s8l0rvyu7mls1gv3a56 manager:2377

docker container exec -it worker02 docker swarm join --token SWMTKN-1-54n68bqnzn31gldc5c18l554fzk78g9p5xf08jff7781nhr5y4-4qp857s8l0rvyu7mls1gv3a56 manager:2377

docker container exec -it worker03 docker swarm join --token SWMTKN-1-54n68bqnzn31gldc5c18l554fzk78g9p5xf08jff7781nhr5y4-4qp857s8l0rvyu7mls1gv3a56 manager:2377

# swarmクラスタの状態を確認
docker container exec -it manager docker node ls

# overlayネットワークを構築し、立ち上げたコンテナ群が同一ネットワーク内にいるよう擬制する
docker container exec -it manager docker network create --driver=overlay --attachable todoapp

# dockerイメージを生成
docker image build -t ch04/tododb:latest .

# イメージのpush/pull先のレジストリを設定。今回はdindのregistryコンテナ
docker image tag ch04/tododb:latest localhost:5000/ch04/tododb:latest

# イメージをpush
docker image push localhost:5000/ch04/tododb:latest

# swarmによるデプロイ
docker container exec -it manager docker stack deploy -c /stack/todo-mysql.yml todo_mysql

# デプロイされたレプリカを確認
docker container exec -it manager docker service ls

"docker stack deploy" 実行時に "hoge Additional property hoge is not allowed" というエラーが出た時

事象

"docker stack deploy"実行時に "hoge Additional property hoge is not allowed" というエラーが出たので自分用にメモ。

原因

"Additional property is not allowed" とあるように、ymlファイルに記述したプロパティがサポートされてないものだから。 だいたいはtypoが原因。

× images ○ image

× enviroment ○ environment

読書メモ 『confident ruby』

どんな本か

Rubyのメソッドはいったいどのような要素で構成されているのかを解明し、より責務が明確で見通しを立てやすいコードを書く技を学ぶことを目的とした本

どんな内容か

1章

  • メソッドの中身は必ず以下の4要素に分類できる
    1. Collecting input :入力値の収集
    2. Performing work :中核となる働きを行う
    3. Delivering output :結果を出力する
    4. Handling failures :エラーハンドリングを行う
  • 4要素がごちゃごちゃっと混在しているメソッドは脆く、読みにくい
  • 読みやすいメソッドというのは、以下の順番で動作する
    1. 入力値を収集し、
    2. 中核となる動作を行い、
    3. 結果を出力し、
    4. 最後にエラーハンドリングをする

2章 Performing Work

  • work(働き)という要素の構造は、このメソッドがどんな責務を負っているかによって決定づけられる
  • OOPのコアとなる特徴は、「メッセージの送信」であり、プログラムの働きというのはつまるところ、オブジェクトにメッセージを送る一連の流れのことである
  • OOPを行うということは、どんなメッセージが送られ、どんな環境下で、どんなオブジェクトが各メッセージを受け取るかを意思決定していくことである
  • 保守性があるコードを書くとは、一度に一つのことに注意を向けるようにし、一度に一つのレベルの抽象度の物事について思考できるようにすることである
  • これらを実現するには以下の3つが必要であり、既存メソッドを分析する際もこの視点を持つとよい
    1. タスクを実現できるために送るメッセージの特定
    2. そのメッセージに対応するロール(役割)の特定
    3. そのロールを担えるオブジェクトを、メソッドがしっかりと受け取ることの保証
  • ロールとは、「責務の集合」である。クラスとは別の概念。
  • レシーバに送るメッセージと、レシーバの責務とがそれぞれ対応する形で分解できる。 -例: lagecy_data_parser(ロールをもつレシーバ)に対し、#parse_legacy_purchase_records(メッセージ)に応答するよう求める -例:customer_listに、#get_productに応答するよう求める
  • ロールとはダックタイピングにおけるダックのこと。ダックタイプには2つの落とし穴がある
    1. ダックの正体を見誤ること
    2. 型チェックを行うこと。nil?でNilClassのチェックをしているのも型チェックに他ならない
  • 多くのifやcaseが登場し出したら、そのオブジェクトは1種類以上の役割を果たそうとする複数責務を持つものだと疑うべき

3章 Collecting Input

  • 入力値を受け取らないメソッドというのは存在せず、もし完全に入力値を受け取らないならそれは定数で表現できるはずだ
  • 一番わかりやすい入力値の形式はメソッドの引数。他にも定数や他のメソッド、インスタンス変数なども入力値の一つ。
  • 普段はクラスのことをトップレベル定数とは捉えないし、入力値の一つとしても捉えないが、クラスも立派な入力値の一つである

ターミナルに過去打ち込んだコマンドをcronで自動記録してみた

結論

ターミナルに打ち込んだコマンド履歴を自動で取得してGithubリポジトリにpushするようにした

目的

  • 作業の振り返りをしやすくしたい
  • コマンドのメモを手動で取るのは面倒だし忘れがち
  • 自動化したい

詳細

  • 実行環境 Centos7.7

コマンド履歴を記述するシェルスクリプトの作成

  • historyコマンドでコマンド履歴を出力 -> 履歴を追記し、日付をファイル名末尾に付けて保存
  • 自動でGitリポジトリにpush
  • task.shファイルを作成して以下を記述
#!/bin/sh
cd /home/{ユーザー名}/memo
history -r ~/.bash_history
history -a
history > history_$(date '+%Y%m%d').log
git add history_$(date '+%Y%m%d').log
git commit -m "history_commit"
git push

cronie-noanacronの導入

  • 指定時間ちょうどにジョブを実行させるにはcronie-noanacronをインストール必要があるので導入
  • cronie-anacronが既に入っていますが、アンインストールします
sudo yum -y install cronie-noanacron
sudo yum -y remove cronie-anacron

crontabの設定

  • crontabで定期実行のタイミングと、実行対象のファイルを指定
sudo crontab -u {ユーザー名} -e
  • エディタが開くので以下を記述。毎日19時にtask.shを実行するよう今回は指定
0 19 * * * /home/{ユーザー名}/memo/task.sh

結果

こんな感じでリポジトリに過去のコマンド履歴を保管できます f:id:dimn_zkym:20200121231905p:plain

所感

  • シェルスクリプトはほとんど書かないのでもっと上手なやり方がありそう
  • 当初は実行したい処理をcrontabのエディタ内に直接書いていたが、crontab実行時の挙動が怪しかったのでシェルスクリプト化した
  • historyコマンドで取得する履歴を当日に限定できれば、リポジトリ内に保存するテキストを見やすくできそう
  • この記事もリポジトリにpushしたコマンド履歴ファイルを見ながら書けたので、振り返りのための仕組みとしてはいい感じ