OmniAICreator/Qiita-1.07M huggingface dataset:
コードレビューの大切さを知ろう


こんにちは！ 最近、Rustを始めた[hihumikan](https://qiita.com/hihumikan)です。
この記事は[Volare Advent Calendar 2021](https://qiita.com/advent-calendar/2021/volare)の14日目の記事です。


## これ何？


この記事は、**コードレビューという考え方を知ってほしい**という気持ちを書き記したものです。


## 何が起こった？

自分はチーム開発でリーダから出されていたissueを片付けていました。

その時はまだ言語に対して未熟で、何が正しいのか、何が良いコード分からない状態でコーディングをしていました。

何だかんだで作り上げた「動く」コードをpull requestで出してみた。

間も無くして、コードの指摘の数を見ると10個程度来ていた。

ネストの深いコード、命名など色々指摘されました。

それらを見ていくと段々、何でこんなに否定的に指摘されているように感じてきてしまい、

「一生懸命やったのに...」「何でこんなに言われるんだ？？？」「じゃぁ指摘するんだからレビュアーが直せよ！」という思考に。

また、そんなにチーム開発を積んできたわけでもない、素人の自分からしたら、動けば良くね？って感じてしまいました。

バグも無ければ、その**仕様通りに動かせる事が可能**であり、何も問題がないはずです。


しまいには**レスバトルを勃発させる要因**になりました。


## コードレビューって何をするの？

何で動くのにコードレビューが必要なのでしょうか？

相手側から見た場合、

1. 良いコードでコーディングをして、メンテナンス性の良いコードを作り上げたい
2. バグを生みやすいコードは避けたい。
3. 書く必要のない部分を残しておきたくない。簡潔にしておきたい。

こうすることによって、**設計変更やバグに対して強くなります。**

コードレビューはこの観点を持ってコードを一つ一つ見て、チェックします。

当時の自分は知らなかったんですが、知識も居るし大変な作業で、毎回そこのブランチに移動して変更箇所とちゃんと動作しているのかをチェックするんです。そして、ダメなところをチェックして理由を付けて返さなきゃいけないんです。

**コードの品質が悪くなる、バグの温床になるから見ているんです**。

コードレビューはそういう目的をもって行なっています。

## コードレビューの考え方

コードレビューをすることには**良いコードを生み出す事という目的があるから**必要です。

動けば良いってもんじゃないからやっています。

この気持ちのすれ違い、動く動かないではなく、**良いコードを目指す**というコードレビューという趣旨が理解していなかったのでレスバトルになったのではないかと思います。

## まとめ

- 動けば良いってものじゃない
- コードレビューの側面を知って、チームと共通化しよう
- 知識不足は仕方ない
- 愛着のある一生懸命やって作り上げたコードは誰しも、その一生懸命さを否定していないぞ

ありがとうございました。
専用サーバの移行手順

使用している専用サーバを老朽化などにより新しい専用サーバに移行することになった場合の手順を書き起こしていきます。
サーバ構成、メールアドレス、メーリングリストについてはサーバー会社が提供する移行ツールの使用を想定します。

## 専用サーバの状態
複数ドメインでWEBサーバ、メールサーバとして使用されている
長い間使われていたため、稼働していない古いモノもある状態

## 移行前の準備
### 期間の設定
上記の通り複数の用途で使われている場合は半年間の期間を設けて作業を行う。

### 利用状況リストを作成
 - 今存在しているサーバをドメイン名でリスト化
 - 各ドメインごとに使用されているメールアドレスをリスト化
 - リストを元に用途と稼働状況を調査する（状況が分からないものについては移行する方針）
 - リストのドメイン名に稼働状況を元に移行するドメインを決める


### バックアップを取る
 - 移行するドメインのWEBサーバ内のファイルをダウンロード
 - データベースを使用している場合はデータをエクスポート

## 移行前の作業を確認する
### WEBサーバ
##### 1. 新しいサーバにバックアップファイルをアップロード
##### 2. 新しいサーバにデータベースを作成し、バックアップデータをインポート
##### 3. hostsファイルを編集しドメインを新サーバipアドレスに変更
##### 4. 表示・動作確認を行い、問題がある場合コードを修正する（古いバージョンのコードなど）

### メールサーバ
サーバ移行のためドメイン切替後、2週間ほどドメインの浸透期間になります。
浸透期間中メールは古いサーバ、新しいサーバどちらにくるかわからない状態になります。
そのため、サーバ移行前にメールクライアントで新旧２つのメールアカウントを用意する必要があります。
##### 1. メールクライアント（Thunderbirdなど）に今使用している同じメールアドレスでアカウントを作成。
##### 2. 作成したアカウントの受信・送信メールサーバー名を新しいサーバーのドメイン名にする
##### 3. 作成したアカウントでメールが送信できるか確認する

### DNS
##### 1. 各ドメインのAレコードを新サーバのipアドレス、MXレコードを新しいサーバ名にする
##### 2. メールを使用するドメインにはSPFレコードを設定する

## 移行作業
#### 利用者向けにメール設定の手順書を作成
 - 全ての画面をスクリーンショット
 - 画像編集で押すボタン、入力する項目などをわかりやすくする

#### 作業開始前 全利用者に告知
 - サーバが移行する旨とその理由
 - サーバを完全に移行する日時
 - 新しいメールアカウントの作成と設定のお願い
 - メール設定の締め切り日時

を記載した告知を行い、メール設定の手順書を配布
#### WEBサーバ
リストを元にタスクを作成、上記の手順通り作業を実施
#### メールサーバ
利用者向けに手順書を作成し、設定をしてもらうよう通知する
不慣れな操作をしてもらうため、問い合わせを想定して余裕を持った仕事を行う

#### 作業終了後 全利用者に告知
・忙しい中設定を行ってくれたお礼
・再度サーバを完全に移行する日時
・ドメイン浸透期間中に新旧どちらかのフォルダにメールが届くため、2週間ほど両方のフォルダを使用してもらうこと
・まだアカウント作成、設定を行っていない方は作業者宛に個人的に連絡のお願いを記載した告知を行う

## ドメイン切替
万が一に備えて影響が少ない深夜にドメイン切替を行う
メールを使用しているドメインはSPFレコードを設定する

リストを元に全てのWEBサーバーの表示・動作確認
メール送信・受信の確認

## ドメイン切替後
浸透期間終了後、利用者向けに新アカウントと旧アカウントのフォルダを統合するための手順書を作成し配布。
（例：Thunderbird アドオン ImportExportTools NG の使い方）

## 終わりに

利用者に忙しい中不慣れな操作を行ってもらうことをお願いするため、
手順書は編集した画像と文章で読解にストレスをかけないように意識して作成する必要があります。

手順書作成するときのソフトは時間をかけずにいい感じのデザインになるマークダウンエディタがおすすめです。

https://typora.io/

非常に使いやすく、マークダウンのため文字の大きさや位置を気にすることなく作成することができます。（昔は無料でしたが、現在は有料みたいです）
iPhone サポートOSバージョン一覧表

## はじめに
12月投稿の記事ですが、アドカレではないです。
社内の端末の整理をする際に困ったので今後のためにメモ
都度更新予定

## 一覧表

| 端末 | 初期バージョン | 最終サポートバージョン |
|:-----------|------------:|-------------:|
| iPhone      | 1.0 | 3.1.3 |
| iPhone 3G   | 2.0 | 4.2.1 |
| iPhone 3GS  | 3.0 | 6.1.6 |
| iPhone 4    | 4.0 | 7.1.2 |
| iPhone 4S   | 5.0 | 9.3.6 |
| iPhone 5    | 6.0 | 10.3.4 |
| iPhone 5c   | 7.0 | 10.3.3 |
| iPhone 5s   | 7.0 | 12.5.5 |
| iPhone 6 / 6 Plus   | 8.0 | iOS15.5 |
| iPhone 6s / 6s Plus | 9.0 | iOS15.5 |
| iPhone SE 第一世代 | 9.0 | iOS15.5 |
| iPhone 7 / 7 Plus | 10.0 | iOS15.5 |
| iPhone 8/ 8 Plus | 11.0 | iOS16.7.10 |
| iPhone X  | 11.0 | iOS16.7.1 |
| iPhone XR | 12.0  | iOS18までサポート |
| iPhone Xs / Xs Max  | 12.0 | iOS18までサポート  |
| iPhone SE 第二世代 | 13.0 | iOS18までサポート |
| iPhone 11 | 13.0  | iOS18までサポート |
| iPhone 11 Pro / 11 Pro Max  | 13.0 | iOS18までサポート  |
| iPhone 12 | 14.0  | iOS18までサポート |
| iPhone 12 mini | 14.0  | iOS18までサポート |
| iPhone 12 Pro / 12 Pro Max  | 14.0 | iOS18までサポート |
| iPhone 13 | 15.0 | iOS18までサポート |
| iPhone 13 mini | 15.0 | iOS18までサポート |
| iPhone 13 Pro / 13 Pro Max  | 15.0 | iOS18までサポート |
| iPhone 14 / 14 plus / Pro / Pro Max | 16.0 | iOS18までサポート |
| iPhone 15 / 15 plus / Pro / Pro Max | 17.0 | iOS18までサポート |
| iPhone 16 / 16 Plus / Pro / Pro Max | 18.0 | iOS18までサポート |
| iPhone 16e | 18.3 | iOS18までサポート |
jinc関数の畳み込み演算

@koshian2さんの記事でFFTした画像にガウスフィルタを畳み込みしたコードが示されていた。
このコードを拝借して周波数領域で[jinc関数](https://en.wikipedia.org/wiki/Sombrero_function)を畳み込んだ結果がどうなるかを示してみたい。

https://qiita.com/koshian2/items/7184cfa790e1713842f5


#コードの変更
従来のガウシアンカーネルの代わりにjincカーネル、sincカーネルを定義し代わりに使用する。

```python
    # 画像処理のガウシアンぼかしのカーネル
    gaussian_kernel = torch.FloatTensor([[1, 2, 1], [2, 4, 2], [1, 2, 1]]) / 16.0

    # jinc,sinc関数のカーネル
    import numpy as np
    from scipy.special import jv, sinc
    x = np.linspace(-32, 32, 65)
    y = np.linspace(-32, 32, 65)
    X, Y = np.meshgrid(x, y)
    jinc_kernel = jv(1, 2*np.sqrt(X**2+Y**2))/(np.sqrt(X**2+Y**2))
    jinc_kernel[32,32] = 1.0
    jinc_kernel = torch.FloatTensor(jinc_kernel)
    sinc_kernel = torch.FloatTensor(sinc(0.7*X)*sinc(0.7*Y))
```

また、周波数空間で最大強度が中央にくるようにFFT=>シフト=>畳み込み=>シフト=>IFFTとなるように追加でシフトを行っておく。

```python
    fft = torch.fft.rfftn(orig, dim=(-2, -1), norm="ortho") # (1, 3, 512, 257): 複素数
    fft = torch.fft.fftshift(fft)
    blur_real = F.conv2d(fft.real, conv_kernel, padding='same')
    blur_imag = F.conv2d(fft.imag, conv_kernel, padding='same')
    fft       = torch.fft.ifftshift(fft)
    blur_real = torch.fft.ifftshift(blur_real)
    blur_imag = torch.fft.ifftshift(blur_imag)
```

#結果
**jinc関数の場合**
![jinc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299928/dd726bba-838d-a03e-a772-214e675ff786.png)
**sinc関数の場合**
![sinc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299928/2dcbb92f-1044-7a36-f2ee-a4bb59f75522.png)

#まとめ
結果は画像にマスクを掛けたようになった。jinc関数は円形、sinc関数は正方形のマスクを掛けた結果に見える。これはsinc関数のFFTがrect関数(矩形関数)であり、周波数領域の畳み込みは実空間でのrect関数の積(画像のマスク)に等しいからである。また、実空間でこれらのフィルタを畳み込むと、周波数空間ではローパスフィルタを掛ける事になる。
最近jinc関数を知ったのだが、これがsinc関数と比べどう違うかを確認できた。
Microsoft SentinelからPower BI

# はじめに
　[Microsoft Enterprise Mobility + Security (EMS)](https://www.microsoft.com/ja-jp/security/business/enterprise-mobility-security)と関連するSIEMサービスである、[Microsoft Sentinel](https://azure.microsoft.com/ja-jp/services/azure-sentinel/#overview)について、KQLでログを引っ張ってくるところを、Power BIのPower Query エディターに連携する、というところの概要を説明します。

## リファレンス

https://docs.microsoft.com/ja-jp/azure/sentinel/powerbi?WT.mc_id=DP-MVP-5004418

## Power BIへエクスポート（M Query）

ログのクエリを実行します。そして、エクスポートのメニューから「Power BIへエクスポート（M Query）」を選択すると、クエリがのテキストファイルがダウンロードされます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419691/63f41443-1dde-c566-5127-0bc3b0fcbe15.png)

そのテキストの内容をクリップボードに持っておきます。

## Power BI Desktopでの読み込み

Power Query エディターを起動します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419691/b2f24eb1-4ea2-45b7-8d23-9c4f17732105.png)

「空のクエリ」を呼び出して

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419691/83047a8f-b9ad-9614-ecc5-ab8a9018bdc9.png)

「詳細エディター」を呼び出し

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419691/bce9131b-0768-41ae-b3d5-dcd1f8b9303b.png)

全部を、テキストの内容に置き換えてください。
認証が必要になった場合は、実行してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419691/b1b0f640-e8bc-54a9-364a-a4f35f0cc942.png)

このように読み込まれます。あとのビジュアル作りは通常と同じです。

## Power BI Serviceのデータフローでの読み込み

データフローを起動し、新しいテーブルの定義をします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419691/4b0df749-7ee1-277e-3a8c-137089b61ebd.png)

Power BI Desktopと同様に「空のクエリ」を選択し、全部をエクスポートした内容で書き換えます。そして[次へ]

コメントの部分のスクリプトで同意画面がでましたが、OKであれば[同意]を

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419691/278f451d-5de3-180a-c3ce-7e638be39052.png)

認証が必要になるので、[接続の構成]ですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419691/87be3e69-e1c7-a0b7-342b-b0714b08ea19.png)

サインインします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419691/c16fd659-f5de-cfba-84eb-78f958c97b9b.png)

データが読みこまれました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/419691/77448da8-ed9e-78ca-ca38-c4ea50512647.png)


Power BIに慣れている方は、Sentinelのデータをこのように容易に取得することができます。

# おわりに

　[日本のサイト](https://azure.microsoft.com/ja-jp/services/azure-sentinel/#overview)、まだ旧名 Azure Sentinelですね。そして、QiitaのタグもAzureSentinelを皆さん使われていますね。製品名が変わると、タグはどうしたらいいんだ、というところは悩ましいですね。
でも悩んでもしょうがないので新しいのを作って加えました。
SwiftUIを業務で使いはじめて約１年が経過したので知見をまとめる

#はじめに
昨年からSwiftUIを使いはじめて、約１年が経ちました。その間様々な問題が発生し、例えば値が変わっても自動で画面が更新されなかったり、予期せぬ動作や表示が崩れたりといったことがありました。それらの解決策や注意する点をまとめていきます。

#画面更新
まずは画面更新にまつわる注意点をあげます。基本的にSwiftUIでは`@State`や`@Binding`を変数につけることで、値が変わった時に画面に更新処理が入ります。
###forEach
SwiftUIではループ処理として`ForEach`を使います。引数には`Range<Int>`型を入れ、その間の分だけ繰り返すようになっています。
しかし、

```swift
@State range = 0..<5
.
.
.
range = 6..<10
ForEach(range) { index in
// 繰り返したい処理
}
```
のようなコードは`range`が変更されても`index`は0から5までにしかなりません。この場合は引数に`id`を入れる必要があります。

```swift
ForEach(range, id: \.self) { index in
// 繰り返したい処理
}
```
ForEachにはもう一つ気をつける点がありまして、iOSのバージョンによっては`Range`が変数でなくても、更新されない時があります。その場合でも上記のコードのように引数に`id`を入れれば解決します。

今のところ、`id`をして不具合を見つけていないので、迷ったらつけてしまった方がいいような気がします。

###他View間は更新されない

```swift
VStack {
   ContentView1()
   ContentView2()
}
```
このような時、端末上では一つのViewとして表示されています。しかし、`ContentView2`で値が更新されても`ContentView1`は更新されません。当たり前かもしれませんが、案外見落としてしまうところでもあります。
ちなみに`NotificationCenter`などで更新したいViewの`ObservableObject`で更新処理を走らせれば大丈夫です。

```swift:ContentView1
@ObservedObject var viewModel: ContentView1Model
viewModel.objectWillChange.send()
```

#その他
`.sheet`や`.alert`は複数使う時には注意が必要です。

```swift
VStack {
}
.sheet {
// 表示したいView
}
.sheet {
// 表示したいView
}
```
とやると片方しか表示されません。なので

```swift
Color.clear
   .sheet
```
のように`.sheet`を複数回使う時は都度`Color.clear`などのViewを用意しないと表示されません。（他の方法もありますが割愛します。）

#不具合
###ios13
ios13はそれより後と比べ挙動が異なることが多かったです。例えば、上記のその他で使ったような`Color.clear`を使った場合、ios13ではフレームが幅高さ両方とも0にはなりません。なのでViewに白いスペースができてしまうのです。
こちらは`frame(width: 0, height: 0)`とすれば問題なくなります。

他にも`.alert`が機能していなかったり、ios14以降とはViewのサイズが異なっていたり、文字サイズが崩れたりと結構ありました。ios13にも対応する場合は何度も確認した方がいいですね。(足切りしてしまった方が安全ですが。)


###EnvironmentObject
2個前のViewに戻りたい時にフラグを保存するために使っていたのですが、画面が行ったり、きたりとおかしな挙動をしました。この時はシングルトンを使うと正常な動作になりました。ちなみに今のところ、シングルトンで問題は起きていません。
参考: https://stackoverflow.com/questions/61363361/environmentobject-vs-singleton-in-swiftui

#おわりに
とにかくios13にはかなり苦労させられました。（まあ13での確認を怠ったことが悪いのですが）皆様もお気をつけください。気づいた時にはViewはぐちゃぐちゃ、クラッシュまみれなんていうことになりかねません。
TypeScriptで2つ選ぶ組み合わせの生成をワンライナーでやる

# 目的

TypeScriptで配列内の2つの要素の組み合わせをワンライナーでやります。
3つ以上は他の記事を参考にしてください。

# 実装

```ts
const arr = [...Array(5).keys()];
const comb = arr.map((v, i) =>
    arr.map((g, j) =>
        i < j ? [v, g] : undefined
    ).filter((coupling): coupling is number[] => coupling !== undefined)
).flat();
```

内部の`map`ではnumber[]とundefinedの二つが生成されるため、filterでundefinedをはじきつつ型ガードしています。
[参考](https://qiita.com/kgtkr/items/b793c62c44e81b3b73a1)

出来ているか見てみましょう。

```ts
console.log(comb);
```

```
[
  [ 0, 1 ],
  [ 0, 2 ],
  [ 0, 3 ],
  [ 0, 4 ],
  [ 1, 2 ],
  [ 1, 3 ],
  [ 1, 4 ],
  [ 2, 3 ],
  [ 2, 4 ],
  [ 3, 4 ] 
]
```

出来ていますね！

# おまけ

```ts
const 虹ヶ咲学園スクールアイドル同好会: string[] = [
    '高咲侑',
    '上原歩夢',
    '中須かすみ',
    '桜坂しずく',
    '朝香果林',
    '宮下愛',
    '近江彼方',
    '優木せつ菜',
    'エマ・ヴェルデ',
    '天王寺璃奈',
    '三船栞子',
    'ミア・テイラー',
    '鐘嵐珠'
];
const カップリング = 虹ヶ咲学園スクールアイドル同好会.map((v: string, i: number) =>
    虹ヶ咲学園スクールアイドル同好会.map((g: string, j: number) =>
        i < j ? [v, g] : undefined
    ).filter((coupling): coupling is string[] => coupling !== undefined)
).flat();

console.log(カップリング);
```

```
[
  [ '高咲侑', '上原歩夢' ],      
  [ '高咲侑', '中須かすみ' ],    
  [ '高咲侑', '桜坂しずく' ],    
  [ '高咲侑', '朝香果林' ],      
  [ '高咲侑', '宮下愛' ],        
  [ '高咲侑', '近江彼方' ],      
  [ '高咲侑', '優木せつ菜' ],    
  [ '高咲侑', 'エマ・ヴェルデ' ],
  [ '高咲侑', '天王寺璃奈' ],    
  [ '高咲侑', '三船栞子' ],
  [ '高咲侑', 'ミア・テイラー' ],
  [ '高咲侑', '鐘嵐珠' ],
  [ '上原歩夢', '中須かすみ' ],
  [ '上原歩夢', '桜坂しずく' ],
  [ '上原歩夢', '朝香果林' ],
  [ '上原歩夢', '宮下愛' ],
  [ '上原歩夢', '近江彼方' ],
  [ '上原歩夢', '優木せつ菜' ],
  [ '上原歩夢', 'エマ・ヴェルデ' ],
  [ '上原歩夢', '天王寺璃奈' ],
  [ '上原歩夢', '三船栞子' ],
  [ '上原歩夢', 'ミア・テイラー' ],
  [ '上原歩夢', '鐘嵐珠' ],
  [ '中須かすみ', '桜坂しずく' ],
  [ '中須かすみ', '朝香果林' ],
  [ '中須かすみ', '宮下愛' ],
  [ '中須かすみ', '近江彼方' ],
  [ '中須かすみ', '優木せつ菜' ],
  [ '中須かすみ', 'エマ・ヴェルデ' ],
  [ '中須かすみ', '天王寺璃奈' ],
  [ '中須かすみ', '三船栞子' ],
  [ '中須かすみ', 'ミア・テイラー' ],
  [ '中須かすみ', '鐘嵐珠' ],
  [ '桜坂しずく', '朝香果林' ],
  [ '桜坂しずく', '宮下愛' ],
  [ '桜坂しずく', '近江彼方' ],
  [ '桜坂しずく', '優木せつ菜' ],
  [ '桜坂しずく', 'エマ・ヴェルデ' ],
  [ '桜坂しずく', '天王寺璃奈' ],
  [ '桜坂しずく', '三船栞子' ],
  [ '桜坂しずく', 'ミア・テイラー' ],
  [ '桜坂しずく', '鐘嵐珠' ],
  [ '朝香果林', '宮下愛' ],
  [ '朝香果林', '近江彼方' ],
  [ '朝香果林', '優木せつ菜' ],
  [ '朝香果林', 'エマ・ヴェルデ' ],
  [ '朝香果林', '天王寺璃奈' ],
  [ '朝香果林', '三船栞子' ],
  [ '朝香果林', 'ミア・テイラー' ],
  [ '朝香果林', '鐘嵐珠' ],
  [ '宮下愛', '近江彼方' ],
  [ '宮下愛', '優木せつ菜' ],
  [ '宮下愛', 'エマ・ヴェルデ' ],
  [ '宮下愛', '天王寺璃奈' ],
  [ '宮下愛', '三船栞子' ],
  [ '宮下愛', 'ミア・テイラー' ],
  [ '宮下愛', '鐘嵐珠' ],
  [ '近江彼方', '優木せつ菜' ],
  [ '近江彼方', 'エマ・ヴェルデ' ],
  [ '近江彼方', '天王寺璃奈' ],
  [ '近江彼方', '三船栞子' ],
  [ '近江彼方', 'ミア・テイラー' ],
  [ '近江彼方', '鐘嵐珠' ],
  [ '優木せつ菜', 'エマ・ヴェルデ' ],
  [ '優木せつ菜', '天王寺璃奈' ],
  [ '優木せつ菜', '三船栞子' ],
  [ '優木せつ菜', 'ミア・テイラー' ],
  [ '優木せつ菜', '鐘嵐珠' ],
  [ 'エマ・ヴェルデ', '天王寺璃奈' ],
  [ 'エマ・ヴェルデ', '三船栞子' ],
  [ 'エマ・ヴェルデ', 'ミア・テイラー' ],
  [ 'エマ・ヴェルデ', '鐘嵐珠' ],
  [ '天王寺璃奈', '三船栞子' ],
  [ '天王寺璃奈', 'ミア・テイラー' ],
  [ '天王寺璃奈', '鐘嵐珠' ],
  [ '三船栞子', 'ミア・テイラー' ],
  [ '三船栞子', '鐘嵐珠' ],
  [ 'ミア・テイラー', '鐘嵐珠' ]
]
```

あ＾～
新卒エンジニアが先輩に指摘されたこと5選

# はじめに
2022年も同じことを指摘されないように、先輩に指摘していただいたことをまとめてみました。
入社してから9ヶ月も経ち、2021年も終わるなんて...時の流れは本当早いですね。

# 1. 関数の引数はできる限り減らす
### 引数なし
```php:index.php
public function getValueByCurrentRoute()
{
    $route_values = [
        'a_route' => 'A',
        'b_route' => 'B'
    ];
    $current_url = url()->current();
    
    return $route_values[$current_url];
}

$value = getValueByCurrentRoute();
```

### 引数あり

```php:index.php
public function getValueByCurrentRoute($current_url)
{
    $route_values = [
        'a_route' => 'A',
        'b_route' => 'B'
    ];
    
    return $route_values[$current_url];
}

$current_url = url()->current();
$value = getValueByCurrentRoute($current_url);
```

どちらも同じことをしていますが、どちらの場合が使い勝手が良いでしょうか。明らかに引数がない時の場合だと思います。なぜなら、引数にどのような値を取らなければならないのかを考える必要がないためです。実際は今回の例のように単純ではありませんが、複雑なコードを実装しているとついつい本来は不要である引数を関数に持たせてしまうことがあるので、**関数の引数はできる限り減らす**ことが大事です。

# 2. 関数・メソッドの行数を意識する
複雑な処理をする関数・メソッドの行数はどうしても長くなりがちです。そのような時は処理単位でメソッドを作成することで、可読性を大きく向上させましょう。

```php:index.php
class HappyBirthday 
{
    // 処理が長すぎて読みづらいパターン
    public function makeChocolateCake()
    {
        // チョコを作成する処理をここから20行書く
        // スポンジを作成する処理をここから20行書く  
    }
}

class HappyBirthday
{
    // 可読性を考慮しているGoodなパターン
    public function makeChocolateCake()
    {
        // チョコを作成する処理
        $chocolate = $this->makeChocolate();
        // スポンジを作成する処理
        $sponge_cake = $this->makeSpongeCake();
    }

    private function makeChocolate()
    {
        // ここに20行
    }

    private function makeSpongeCake()
    {
        // ここに20行
    }
}  
```

では、「1つの処理単位が何行だったらレビュワーは読みづらいと感じてしまうのか」については、[こちらの記事](https://qiita.com/kamykn/items/d827c17c849bd111d272)がおすすめです。

# 3. 「それ、getじゃなくてfetchにしよっか」
getXXXとfetchXXXというメソッドはどちらもXXXを取得することを表していますが、「どのようにXXXを取得するか」という点で異なります。メソッド名は、読み手にそのメソッドが「どのように何をするのか」を伝える役割を担っているので、**メソッドの目的や手段がすぐに理解できるようなメソッド名**を心がけましょう。以下、おすすめ記事です。

* [関数名によく使われる英単語（動詞）の意味とニュアンス](https://php-archive.net/php/words-in-function-names/)
* [うまくメソッド名を付けるための参考情報](https://qiita.com/KeithYokoma/items/2193cf79ba76563e3db6)
    
# 4. if文だけでなくswitch文も検討する
同じ変数を異なる値と比較し、値に応じて異なったコードを実行する時はswitch文の方が見やすいので、if文ばかりを使わないようにしましょう。以下のコードは公式からの引用です。

```php:index.php
if ($i == 0) {
    echo "iは0に等しい";
} elseif ($i == 1) {
    echo "iは1に等しい";
} elseif ($i == 2) {
    echo "iは2に等しい";
}

switch ($i) {
    case 0:
        echo "iは0に等しい";
        break;
    case 1:
        echo "iは1に等しい";
        break;
    case 2:
        echo "iは2に等しい";
        break;
}
```

# 5. コメントをつけるときは誰がいつ見てもわかるようにする

```php:index.php
// 不適切な値(1,2)が入る可能性があるため回避する
if (in_array($value, [1, 2])) {
```

このコードを'今'書いている人は「何が不適切なのか」理解していますが、それ以外の人にとってはどうでしょうか。おそらくこのコメントからだけでは「何が不適切なのか」が理解できません。そのため、その人は不適切である理由を調べなければなりません。このようなことを防ぐために**コメントは具体的に**、この場合「何が不適切なのか」をきちんと書くべきです。また、コメントが無くても理解できるコードをなるべく書く力も大切です。

# さいごに
指摘していただいた時、いつも思うのは**「先輩みたいになりたい！！」**。
カスタムユーザー使っている時のAdminの継承先はUserAdminにするといいよ？

django admin　使っていこうぜ！
まいど、東京に来ても関西弁が抜けないstbtkhrです。

今日は小ネタ的な、備忘録的なことを書いていきます。

## カスタムユーザーAdminでadmin.ModelAdminを使うのはダメ

みなさんdjnagoでカスタムユーザーって使っていますか？
いろんな記事を見ていると、admin構成時以下のようにしている記事が見受けられました。
ちなみにアプリ名はaccountにしています。

```python:account/admin.py
from django.contrib import admin
from .. import models

@admin.register(models.User)
class UserAdmin(admin.ModelAdmin):
    list_display = (
        'id',
        'email',
        'username',
)
# ...etc...
```

このadmin.ModelAdminを継承すると実はアドミンのuser画面のdjango本来の実力をだせません。
特に顕著な違いは、ここのユーザー画面を出した時です。
admin.ModelAdminを継承した場合のユーザー画面は以下の通り!

![ユーザー画面1](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/516317/109eca8f-c52e-212f-2f44-6d789129e9c3.png)

現状のままだとpdkdf2.....と続いているのがわかりますね？これはハッシュ暗号化されているということをさします。
パスワードを変更して保存してみてください。
一応仮にtestと書いておこう。そして保存！

![ユーザー画面2](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/516317/86a8266e-7223-1bf1-9329-09db4bfa4e5d.png)

・・・あれれ？ハッシュ化されてへんやん・・・。DB上も他のパスワードとみても全然ちがいます。
![DB](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/516317/4ee05445-ffe2-bb4e-b428-d8789abe0fcf.png)

## では、どうすべきか

これはなぜ起こるのかというと、継承先のadmin.ModelAdminのformがデフォルトのformを使っているからです。
admin.ModelAdminだけでは、django上だとユーザー画面もただのDB上の一画面でしかないのです。

ちなみにdjango_adminのpasswordは、charFieldでDBに記録されます。
特にユーザー用にカスタマイズされたfieldsではないので、djangoのDB操作もただの文字として認識するだけです。

では、継承先をUserAdminにしてみてください。正確には、django.contrib.auth.adminの中にあるUserAdminです。

```python:account/admin.py
from django.contrib import admin
# ↓追加
from django.contrib.auth.admin import UserAdmin

from .. import models


@admin.register(models.User)
# ↓変更
class UserAdmin(UserAdmin):
# 省略
```

そうするとどうでしょう。
![ユーザー画面3](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/516317/50a2e6fb-8e79-c12d-463d-702291048dfa.png)

パスワード専用の画面になっているでしょう！
パスワードもハッシュ化されて保存できるはずです！

これで、また一つdjangoの知識が深まりました！

また機会あれば更新していきます。
自作バルクアップデート


## ざっくりBULK INSERT（バルクインサート）の説明

複数のINSERT処理を行う場合、1行ごとにINSERT文を書いてSQLを流すことで処理することができます。

```sql
/* 例文 */
INSERT INTO users (name, age) VALUES ('tanaka', 23);
INSERT INTO users (name, age) VALUES ('suzuki', 29);
INSERT INTO users (name, age) VALUES ('makihara', 26);
```
ただ、上記の記述ではSQLが1行1行処理されるためデータの数が多くなる程処理が重くなっていきます。

それを軽くするのがバルクインサート
1行にすべてのINSERT文をまとめて書くことで処理が軽くなっていきます

```sql
/* 例文 */
INSERT INTO users (name, age) VALUES ('tanaka', 23),('suzuki', 29),('makihara', 26);
```


## BULK INSERTを真似してBULK UPDATE（バルクアップデート）をしたい


https://damepg.hatenablog.com/entry/2012/10/01/001223

上記のサイトのコードをもとにBULK INSERT文を書き換えて、
`UPDATE INTO users (id, name, age) VALUES (1, 'tanaka', 23),(2, 'suzuki', 29),(3, 'makihara', 26);`みたいに出来ればいいだけどなーと思ったけども
そんなに甘いわけではなかったみたいでいろいろ調べてみたところ

https://qiita.com/maxima/items/ee87d9bea31eddf667e0#:~:text=%E3%83%90%E3%83%AB%E3%82%AF%E3%82%A2%E3%83%83%E3%83%97%E3%83%87%E3%83%BC%E3%83%88%E3%81%A8%E3%81%AF%E3%80%811,UPDATE%20%E6%96%87%E3%81%AE%E3%81%93%E3%81%A8%E3%81%A7%E3%81%99%E3%80%82

上記の記事に書かれていたREPLACEに書き換えるやり方が楽そうだなぁーと思いつつ、読んでいると
>uniqueキーがかぶった場合の内部挙動は、「UPDATE文」ではなく、「DELETE文 + INSERT文」

この一言でREPLACEでの対応が難しくなってしまいました。
（テーブルの一部のカラムのみ更新をかける処理に使いたいため、DELETED＋INSERTではデータを渡していないカラムのデータが空になってしまうため）

## それではBULK UPDATE

渋々、上記の記事で書かれていたELTとFIELDを使った方法でBULK　UPDATEの処理をしてくれる関数を作成

```php
//関数に渡す配列データの例
$data = [
	['id' => 1, 'age' => 23, 'name' => 'tanaka'],
	['id' => 2, 'age' => 29, 'name' => 'suzuki'],
	['id' => 3, 'age' => 26, 'name' => 'makihara']
];

```

```AppModel.php
/**
 * bulkUpdate
 *
 * @param array $data
 * @return boolean
 */
public function bulkUpdate($data) {
	//対象テーブル名
	$table_name = $this->table;
	//対象カラム名のリスト
	$fields = array_keys($data[0]);

	//idのリスト作成
	$ids = implode(',', array_column($data, 'id'));

	//IDの削除
	$key = array_search('id', $fields);
	unset($fields[$key]);

	//SQL文の作成
	$sql = "UPDATE `{$table_name}` SET ";
	$params = [];
	foreach($fields as $field){
		$values = array_column($data, $field);
		//変更するIDを列挙
		$sql .= " `{$field}` = ELT(FIELD(id, {$ids} ),";

		//対象のカラムのパラメータの数だけ「?」を列挙
		$sql .=   implode(", ", array_fill(0, count($values), '?')) . "),";

		//パラメーターを配列に代入
		foreach ($values as $value){
			$params[] = $value;
		}
	}

	//文末のカンマ削除
	$sql = substr($sql, 0, -1);
	$sql .= " WHERE id IN ({$ids});";

	$query = $this->query($sql, $params);
	if ($query === false) {
		return false;
	}
	return true;
}
```

普通のPHPで使う場合は`$table_name = $this->table;`と`$query = $this->query($sql, $params);`をPHP用に書き換えればつかるかな

これで、処理が軽くなるといいんだけどな。
【Unity】IKで武器と手のずれを解消する

こちらは[Unity Advent Calendar 2021 18日目](https://qiita.com/advent-calendar/2021/unity)の記事です。

購入したアニメーションアセットを使う場合など、既存のモーションを別キャラクタに適応する際に左手と武器などの持ち物の位置がずれることがあります。（両手持ち武器のアニメーションなど）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/938939/64943b06-62e9-64c6-4b0b-c474685e990f.png)
このようなズレはIKを利用することで簡単に修正することができます。

# IKPassの有効化
キャラクタのIK制御を有効にするために`AnimatorのLayerタブ > 設定(歯車マーク) > IKPass`にチェックを入れます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/938939/229a8bc5-b7a3-cfc5-cb0f-a05ca3a08c4d.png)

# 武器に左手の持ち手部分を作る
画像のように左手の持ち手部分に空オブジェクト（**LeftHandPoint**）を作成しておきます。
また、武器自体はキャラの右手に持たせておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/938939/d0816b92-3659-b70b-3dee-ab2a8bceec78.png)

# LeftHandPointの座標へキャラの左手を移動させるスクリプトを作成
```LeftHandMatcher.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LeftHandMatcher : MonoBehaviour
{
    private AvatarIKGoal ikGoal = AvatarIKGoal.LeftHand; // IK制御を行う体の部分（今回は左手）
    [SerializeField] private Transform leftHandPoint;
    private Animator anim;
    public bool isEnableIK = true; // IK制御有効化フラグ
    // Start is called before the first frame update
    void Start()
    {
        anim = GetComponent<Animator>();
    }

    private void OnAnimatorIK() { // OnAnimatorIKはIK制御情報を更新する際に呼ばれるコールバック
        if (!isEnableIK)
            return;

        // 武器に作成したLeftHandPointに、左手を移動させる
        anim.SetIKPositionWeight(ikGoal, 1f);
        anim.SetIKRotationWeight(ikGoal, 1f);
        anim.SetIKPosition(ikGoal, leftHandPoint.position);
        anim.SetIKRotation(ikGoal, leftHandPoint.rotation);
    }
}
```
作成したスクリプトはキャラクタにアタッチします。
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/938939/d915aff6-271d-a27f-18f1-f63df8ff751e.png)
アタッチしたスクリプトの**LeftHandPoint**に先ほど武器側に作成した**LeftHandPointオブジェクト**をアタッチします。
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/938939/2f34ae2c-2348-85b3-4ea4-2c9b6b681201.png)
# 角度・位置の調整
スクリプトアタッチ後、実行してみるとIK制御が有効になっており、左手が持ち手部分に移動していることがわかります。
この時に手の位置や角度がおかしい場合は**Unityを実行状態のまま**LeftHandPointオブジェクトの角度・位置を変更します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/938939/d04a304d-49ee-6f4b-58ca-c2f1512510ac.png)
# 結果
IK制御をすることでモーションそのものを改変することなくズレを解消できました。

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/938939/668081a2-2f54-34fd-ae18-d73da8265c0d.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/938939/3296d702-188b-7221-bdbf-069793d900aa.png)|
|---|---|
|オリジナル|IK制御中|

また、IK制御中は画像のようにモーションを切り替えても、LeftHandPointに左手が追従するため、モーションごとに調整は必要ありません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/938939/acc71887-4371-28cd-58a8-c7b0b5e4d104.png)

今回は武器とのズレ解消にIKを利用しましたが、レバーを引く際に手をレバーに合わせる、宝箱を開けるときに手を箱の蓋に合わせるなど、さまざまはズレ解消に利用できると思います。
自然観測法の理論ー瞬時性に着目した新しい波形解析法　森北出版　飯島泰蔵先生

```{r}

#p.9

fun=function(t){
return(3+2*cos(t)+sin(t^2))  
}

X=seq(0,10,0.1)
Y=fun(X)
s=1
h=0.001

g=function(x){
val=fun(x)
intg=function(y){
return((exp(-y/s)*fun(x+h-y)-exp(-y/s)*fun(x-y))/h)  
}
return(val-integrate(intg,0,Inf)$value)
}

gval=c()
for(i in 1:length(X)){
gval=c(gval,g(X[i]))
}

fval=fun(X)
hval=fval-gval

plot(X,fun(X),xlim=c(min(X),max(X)),ylim=c(min(c(fval,gval)),max(c(fval,gval))),xlab="X",ylab="values",col=2)
par(new=T)
plot(X,gval,xlim=c(min(X),max(X)),ylim=c(min(c(fval,gval)),max(c(fval,gval))),xlab="X",ylab="values",col=3)


```


```{r}

#p.30

#双直交二項関数形

M=5

vm1=function(x,m){
val=0
for(r in 0:m){
val=val+((-1)^r)*choose(M,m-r)*x^r/factorial(r)  
}
return(val)
}

vm2=function(x,m){
val=0  
if(m<M){
for(r in 0:m){
val=val+((-1)^(m-r))*choose(m,r)*exp(-x)*x^(M-r-1)/factorial(M-r-1)  
}
}else{
for(r in 0:(M-1)){
val=val+(1-abs(sign(x)))+((-1)^(M-r))*choose(M,r)*exp(-x)*x^(M-r-1)/factorial(M-r-1)  
}}  
return(val)
}



m1=2
m2=2

#関数の定義：
func=function(x){
y<-vm1(x,m1)*vm2(x,m2)
return(y)
}

#積分区間
a=0;b=100
#n;差分メッシュ
#積分の近似的計算：微分積分要論　学術図書出版　戸田暢茂先生　p.95の例4参照
n=10^4
mesh=a+(b-a)*seq(1,n,1)/n
value=c()
for(j in 1:n){
value=c(value,func(mesh[j]))
}
#積分の結果 (p.82 (3.29))
sum(value)*(b-a)/n


vm1vec=c()
vm2vec=c()

a=0;H=100
X=seq(a,H,1)
for(i in X){
vm1vec=c(vm1vec,vm1(i,m1))  
vm2vec=c(vm2vec,vm2(i,m2)) 
}
vmvec=vm1vec*vm2vec

plot(X,vmvec,xlim=c(min(X),max(X)),ylim=c(min(c(vmvec)),max(c(vmvec))),xlab="X",ylab="values",col=3)

```

```{r}

#p.36

#3.4 波形の遡及展開

M=5

vm1=function(x,m){
val=0
for(r in 0:m){
val=val+((-1)^r)*choose(M,m-r)*x^r/factorial(r)  
}
return(val)
}

vm2=function(x,m){
val=0  
if(m<M){
for(r in 0:m){
val=val+((-1)^(m-r))*choose(m,r)*exp(-x)*x^(M-r-1)/factorial(M-r-1)  
}
}else{
for(r in 0:(M-1)){
val=val+(1-abs(sign(x)))+((-1)^(M-r))*choose(M,r)*exp(-x)*x^(M-r-1)/factorial(M-r-1)  
}}  
return(val)
}

f=function(t){
return(3+2*cos(t)+sin(t^2))
}


s=0.5
times=c(2:20)
tau=1

values=c()

for(time in times){
valvec=c()  
for(ms in 0:M){
#関数の定義：
ams=function(x){
y<-f(time-x)*vm1(tau/s,ms)*vm2(x/s,ms)/s
return(y)
}
#積分区間
a=0;b=1000
#n;差分メッシュ
#積分の近似的計算：微分積分要論　学術図書出版　戸田暢茂先生　p.95の例4参照
n=10^4
mesh=a+(b-a)*seq(1,n,1)/n
value=c()
for(j in 1:n){
value=c(value,ams(mesh[j]))
}
#積分の結果 (p.82 (3.29))
valvec=c(valvec,sum(value)*(b-a)/n)
}
values=c(values,sum(valvec))
}

cbind(f(times-tau),values)


plot(times-tau,f(times-tau),xlim=c(min(times-tau),max(times-tau)),ylim=c(min(c(f(times-tau),values)),max(c(f(times-tau),values))),xlab="t",ylab="values",col=2)
par(new=T)
plot(times-tau,values,xlim=c(min(times-tau),max(times-tau)),ylim=c(min(c(f(times-tau),values)),max(c(f(times-tau),values))),xlab="t",ylab="values",col=3)

```
macOS の ADE をざっくり理解する

この記事は、[Supershipグループ Advent Calendar 2021](https://qiita.com/advent-calendar/2021/supership)の16日目の記事になります。

# はじめに

会社等でユーザーに PC を貸与する際、多くの場合あらかじめ設定が必要となる。これを簡略化するために各プラットフォームは事前構成プログラムを持っており、 Apple は Automated Device Enrollment (ADE) というプログラムを用意している。

※ 少し前までは Device Enrollment Program (DEP) という名前で展開されており、今でも DEP で探したほうがドキュメントが多い。

※2 Apple は教育機関向けプログラムとして Apple Distinguished Educator を展開しており、こちらも ADE と略されるので混乱しないよう注意が必要。

ADE では下記のような機能が提供されている。

- MDM への自動登録
- アカウントや設定の事前構成
- 設定アシスタント（初期設定画面）のスキップ

macOS で ADE を利用するためには下記の要件を満たすことが必要となる。

1. Apple Business Manager (ABM) にサインアップしている
2. 対応した MDM を運用している
3. 対応ディストリビューターから Mac を購入している

3 が一番難易度が高く、対応ディストリビューターから Mac を購入していない場合、 macOS で ADE を利用できない。例えば ADE を利用してキッティングを簡略化するために、途中から ADE に登録できる Mac の購入に切り替えても、それ以前の Mac を ADE に登録することはできない。

# ざっくりとした仕組み

ADE に対応したディストリビューターと契約すると「Appleお客様番号」を取得することができる。 Apple から購入する場合には法人向け Apple Store を契約する必要がある。

この番号を ABM に登録すると機器情報が ABM に登録されるようになる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6078/cdc7c16e-d2c9-1ac1-0589-88bc21f95bdb.png)

ABM には複数の MDM サーバーを登録することができ、 ADE 情報をどの MDM で使用するかを選択できる。（OS ごとに自動で使用する MDM を指定することもできる。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6078/4721fa4a-2744-2cb3-be2b-685c0adc5430.png)

その後 MDM 側で機器に対応した ADE プロファイルを指定すると、 MDM が ABM に ADE プロファイルを登録する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6078/0c3df607-72ef-183a-b07c-90fbbe31cae8.png)

その後、ユーザーが Mac を起動し設定をスタートしたとき、 macOS が自動的に ADE プロファイルを読み、 MDM 登録などの初期設定が行われる。
（Mac は ABM に ADE プロファイルを取得に行くわけではないと思われるが、ここでは ABM に取りに行くように表現している。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6078/87f83123-517a-88e7-c001-8e3dd6180ef1.png)

macOS が取得する ADE プロファイルは MDM のプロファイルとは異なるため、 ADE に用意された項目のみ設定が可能である。

# まとめ

OSやデバイスに用意されている MDM プロファイルは機器ごとに分かれていても基本的な仕組みとしては共通であるのに対し、デバイス登録時のフローは機器・MDMベンダーごとに異なるため、仕組み自体は難しくなくても煩雑になりがちです。

しかし、うまく活用すればデバイス管理のコストがかなり簡略化されるので、きちんと理解し、うまく適用することでエラーが起きにくく効率的な仕組みを構築することができます。各社の公式ドキュメントも充実しており、読めば分かるようになっているので、みなさん頑張りましょう。。。

- [Deploying MDM Enrollment Profiles | Apple Developer Documentation](https://developer.apple.com/documentation/devicemanagement/implementing_device_management/deploying_mdm_enrollment_profiles)

# 宣伝

Supershipグループではエンジニアを積極的に採用しており、情報セキュリティに関する採用も行っています。

ぜひ [Supershipグループ採用サイト](https://supership-hd.jp/recruit/) をご覧ください。
進行やファシリテーション時に役立つかもしれない小ネタ



この記事はビビッドガーデン [Advent Calendar 2021](https://qiita.com/advent-calendar/2021/vivid-garden)の15日目です。


はじめまして、はしまるです。
9月にビビッドガーデンに入社しました。

エンジニアですが、エンジニアというラベルを貼られたくないひねくれ者なので、技術ではない話をします！
箸休めにどうぞ！


私は過去にステージ司会をやっていた時期があったり、現在も副業で物事を人に教えたり、趣味でエンタメプロジェクトを動かしたり、某ライブ配信サービスでひとり喋りをしたりしています。

ということで内容は
　**　「進行やファシリテーション時に役立つかもしれない小ネタ」**
です。


苦手なエンジニアが多いのではないかと思います。
かくいう私も、元々は人前で話すのが非常に苦手でした。
（しかしビビッドガーデンにはそういう人が全然いない・・・！）

何かしら役立つことがあれば幸いです！




# トピック

- 大前提
- ハンドリング
- トークテクニック



# 大前提


大前提として大事だと思うことは

- 「自分で全部やろうとしない」
- 「うまくやろうとしない」
- 「様子をうかがい過ぎない」

です。


**進行する人間 ≠ 決定者**

なので、進行する人間はあくまで交通整理をしていく人で、行き先を一人で決めてズンズン進んでいく人ではないです。
（最終決定者が進行する場合もあるとは思いますが、あまり良くないとされているはずです）


- この流れだと次はこの人に振ればいいかな？
- 誰に振ったらいいか分からないからそれを言おう
- 時間的にもここは巻きめだから深堀りせずサクッといこう

みたいな、
　　**「会のゴール（目的）とそれに向かう全体の流れ」**
のことだけを考えて話を進めていくのがポイントだと思っています。


バラエティ番組などで進行がうまい司会者を思い浮かべると、大体
　　**「自身が喋るのがうまい」というより「人に振るのがうまい」**
ですよね！

かといって、「うまくやらなくては」「ちゃんと他の人に気を使わないと」とするとそれに意識がいってしまって、逆にうまくいきません。
気の利いたコメントとかできなくていいです。



# ハンドリング

上記の大前提の意識ができていたら、

- 時間把握
- 脱線時の回収
- ゴール（目的）に必要な話を引き出す

などのハンドリングスキルも自然とあがってきます。


### 時間把握


決まった時間内でゴールまでもっていくのが進行者の役割ですよね。
でも結構時間チェック、忘れがちだったりします。

個人的なコツとしては

- 大きな区切りごとに時間をチェックする
- 話が発散していたり、細かい話に飛んだタイミングで時間をチェックする
- どれくらいのボリューム話したらどれくらい時間が過ぎているかの体感を身につける（これは慣れ）

があります。
チェックタイミングを決めていれば結構らくちんです。
あとは必要に応じて巻きます（ステージなどでなければ伸ばすことはないでしょう）


### 脱線時の回収

「巻きます」といっても「はいおしまーい！」とはできないので、対応としては

- 目的と関係ない話は切る
- 必要だけど長くなりそうな話は次にとるアクションを決める
- その場で必要な話で、うしろが伸ばせるならまあ伸ばす

とかをやっている気がします。


### ゴール（目的）に必要な話を引き出す

話が発散するのはあるあるですが、逆にするべき話が出ないこともあります。
（特に振り返りとか）

「それっぽい会話をしている」ということで安心感を得ずに、常にその会の目的を忘れないようにするのが結構大事だと思っています。

- 目的につながる話を引き出す
- その話題のキーマンそうな人に話を振る
- そもそもの心理的安全性をあげる

あたりは意識すると良いなと思っています。

# トークテクニック

ここまではまあファシリテーション力高い人なら当たり前だな、といった感じでしょうか。
ここからは話し方について、自分の考えを書きます。

- 声のトーン
- 間と声色で区切る
- 話者の切り替え


### 声のトーン

意識しづらいかもしれませんが、声のトーンは意外と重要です。
その人の声色によって、聴いている人もそのテンションに引っ張られていきます。

某アイドルグループのMCトレーニングでも**「声のキーをあげろ」**という指導がされていたのを拝見しました。

声のトーンが低いとテンションも下がっていきますし、声が低すぎるとそもそも聞き取れない、という事態になることも。

私は人前という場では意識して**キーを2個くらい**上げて話すようにしています。


### 間と声色で区切る

話すのに慣れていないと、間ができると非常に不安になります。
「話す」をやることで精一杯でつらつらと話してしまいます。
しかし区切りがないと、聴いている側もそのまま流してしまいがちに。

話す時でも記事で書くときのように `#` `##`のタイミングで

- 直前に一呼吸入れる
- はっきり強調するように話す
- 声のトーンを上げる

などをすると分かりやすくなりますね。
これはプレゼンなどでも言われることだとは思います。


### 話者の切り替え

これも上記の「区切りがない」の対策になるものです。
聴こえる声を変えるために、他者に話してもらうという方法です。

某音楽番組などでメイン司会者とアナウンサーの2人体制で進行をしているのもそれだと思っています。

具体的には

- 自分が知っていることでも担当の人に話させる
- あまり話していない他の人に振る

などがそれに当たりますね。


## まとめ

色々書いてみましたが、結局は最初に書いた
　　**「会のゴール（目的）とそれに向かう全体の流れ」**
を意識してやっていれば良いんだなというところですね。

ぶっちゃけ慣れが一番早いので、
**「うまくやろうとせず、とりあえずやってみる」**
の精神でいくと吉！
（苦手な私も、ファシリテーターをやったり人にモノを教えるようになってから上達しました）

とはいえ自分もまだまだなので、色々試してみつつ学びつつスキルアップしていきたいです！！！

エンジニアらしくないエンジニアとして、この先生きのこることを引き続き意識していこうと思います。


## 最後に


ビビッドガーデンではコミュニケーションをしっかり取りながら、目的に向かって進んでいける力がすごいメンバーが勢揃いしています。

一緒に働いてくれる仲間も募集しています！

https://herp.careers/v1/vividgarden/KpLywERTT_Wh
再発防止MTGを開こう

サービスを運営していく上で必要なMTG、今回は再発防止MTGについて書いていきます。
- - -



## 不具合、問題の再発防止を目指す
サービスリリース後、ユーザーに不利益のある不具合や問題が発生することがあります。
再発防止策を実施せず、再度同様の問題が発生してしまうと、ユーザーからの信頼を損ねてしまいます。
不利益の有無・問題の大小に関わらず、本番環境で発生してしまった問題は再発防止策を検討・実施する必要があります。
その為のMTGが再発防止MTGです。

## 本番環境で発生した不具合を検知する
MTGの前に、本番環境で不具合が発生していることをいち早く検知しなくてはいけません。

・自ら見つける
・Twitterで探る（弊社ではSlackとTwitterを連携しています）
・お問い合わせ管理ツールで不具合報告を検索する
・サーバ側でエラー検知する

のような経路が想定できますが、**誰が・どんな状況で検知したか**、というのは振り返り材料のひとつになりますのでメモしておきましょう。


## 不具合の埋め込み原因を探る

本番で不具合が発生したら、必要な修正・補填・お知らせなどを行い、事態を収拾させます。
　（※ここでのエスカレーションも大事ですので語りたいですが、別の機会に。。）
一段落したら、なぜ不具合が生まれてしまったのか？という**埋め込み原因**について、
担当者（プランナー、サーバエンジニア、クライアントエンジニアなど）は整理します。

## 再発防止に向けてチケットで管理する
担当者は調査と同時に再発防止のタスクチケットを起票しておきます。
このチケットに埋め込み原因と、その再発防止策を記載します。

記載が完了したら、チェック担当者（別の担当者、またはQAなど）にチケットを渡します。
チェック担当者は、なぜチェック時に見逃してしまったのか？という**見逃し原因**と、その再発防止策まで記載します。


**埋め込み原因**と**見逃し原因**と、２種類の原因があるので、
再発防止策は**不具合を作らないようにする対策**と**見逃さないようにする対策**という2つの軸で検討してもらうのです。

チケットの記載が完了したら、再発防止MTGの事前準備完了です。

## 関係者を集め、対策を練る
MTGの参加者については各担当者である、QA、サーバーエンジニア、クライアントエンジニア、プランナーです。
さらに必要に応じてマーケティング、デザインといったメンバーにも参加頂きます。

ちゃんとMTGまでに記載が完了しているチケットを優先的に取り上げ、再発防止策が妥当かどうか、MTG参加者に説明し合意を得ます。

MTGの進め方の注意点としては、懺悔室や裁判のような雰囲気にさせないことです:joy:
どうしても「ワタシが悪うございました・・:cry:」となってしまいがちだからです。

チケットの記載が完了していなかったり、
再発防止策が妥当でなかったり、（根本解決に至ってない、など）
MTG中にさらなる防止策が思いついたりした場合、次回のMTGに持ち越します。

再発防止策が無事実行されたら、チケットをクローズします。

## MTGの実施タイミング
定期的に、できれば週次で開催しましょう。

チケットの記載はその人にとって宿題となってしまうので、ちゃんと記載する時間を各チームで確保してもらう必要があります。
今週も宿題忘れました～　なんて人がいたりすると、ダラダラしてしまいMTGが崩壊する可能性があるので注意しましょう。


## 別のプロジェクトに共有する
再発防止のチケットに書かれている情報は、他のプロジェクトにとっては宝の山です。
隠すべき情報もあり大変ですが、なるべく再発防止の取り組みは横展開していきたいですね。


## 参考書籍
ソフトウェアではない業界の書籍ですが、
人為ミスを直接要因（管理面）と間接要因（心理面）に種別し、その再発防止策について記載されています。

<span style="font-size: 80%">あらゆる職場ですぐに使える 人為ミスの未然防止手法 A-KOMIK: 人為ミスゼロ実現のための考え方と手法   冨澤 祐子</span>
 https://www.amazon.co.jp/dp/4817196106/
AWS CloudShellでECS Execを実行する

CloudShellでECS Execを実行できることを確認しました。
マネジメントコンソールにログインして運用作業をされている方であれば、CloudShellを利用すれば、踏み台サーバ(SSH対象のタスクと同じVPC上に立てたインスタンス)不要でECSタスクにSSH可能です。
CloudShellにはECS Execに必要なソフトウェア(ecs-cli/セッションマネージャープラグイン)がインストール済みかつ、SwitchRoleが必要な環境では、マネジメントコンソールでSwitchRole済みであればCloudShell内でSwitchRoleする必要はないため、踏み台を普段停止していたり、踏み台へのSSHが多段になっているような環境では、CloudShellで実施すると迅速にECS Execが可能です。
※ECS Execは最大2セッションまでという制限があります

# 手順

マネジメントコンソール右上のプロンプトアイコンをクリックします(ベルの左)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/620712/007f6556-ac20-93df-3312-3043020e4639.png)

SSH対象のタスクIDを確認します。CloudShellにはecs-cliがインストールされてるので、CloudShellでも確認可能です。

```
$ ecs-cli ps --cluster CLUSTER
Name                       State    Ports                      TaskDefinition  Health
CLUSTER/TASK_ID/CONTAINER  RUNNING  10.x.x.x:80->80/tcp        TASKDEF:1       UNKNOWN
```

タスクIDを確認したら(上記のTASK_ID部分の文字列)、ECS Execを実行します。
以下では、ECS Execコマンドを汎用化するために、クラスタ名/タスクID/コンテナ名を変数で外出しして実行しています。
試される場合は各変数に実際のクラスタ名などを入力してください。

```
$ cluster=
$ task=
$ container=
$ aws ecs execute-command --cluster $cluster --task $task --container $container --interactive --command "/bin/bash"
```

上記実行後、以下のように出力され、プロンプトが変われば成功です。

```
The Session Manager plugin was installed successfully. Use the AWS CLI to start a session.


Starting session with SessionId: ecs-execute-command-xxxxxxxxxxxxxxxxx

root@ip-10-x-x-x/# 
```

これで普通にECS Execを実行した状態なので、あとは通常通りタスクで実施したい作業を実施可能です。
カレントディレクトリ以下に存在する全てのファイルを対象に文字列置換する

N番煎じですみませんが、カレントディレクトリ以下に存在する全てのファイルを対象に文字列置換するコマンドのメモです。

環境はubuntu20.04です。基本的には以下のようにすればOK。

>[ディレクトリの中を再帰的に全置換するには](https://hydrocul.github.io/wiki/blog/2016/0912-replace-all-text.html)
> カレントディレクトリのファイル全てを対象にする場合

```bash:bash
grep -rl 置換前 . | xargs sed -i.bak 's/置換前/置換後/g'
```

もし`.bak`ファイルを消したくなった場合は以下を実行する。

```
find . -name "*.bak" | xargs rm
```

# 参考
https://hydrocul.github.io/wiki/blog/2016/0912-replace-all-text.html

https://qiita.com/kkyouhei/items/b4ff839a2f36ba194df3

https://qiita.com/pokari_dz/items/0f14a21e3ca3df025d21
【ABC229】D問題を解いていこう！(C++，解説)

#初めに
皆さんこんにちは！
　競プロの問題の理解を深めるために解説記事を書いていこうと思います！今回は**ABC229 D** です！

気に入っていただけたら**LGTM**して頂けるとうれしいです！
何かあれば**コメント**や[Twitter](https://twitter.com/python05238919)からお願いいたします!

#[D - Longest X](https://atcoder.jp/contests/abc229/tasks/abc229_d)
問題は上の見出しから参照お願いします．
###制約
・$ 1 \leq |S| \leq 2 ×10^5$
・$S$は`X` or `.`の文字
・$0\leq K \leq 2 × 10^5$
・$K \in \mathbb{Z}$ ($K$は整数)

###考察
 1つの文字ごと`.`を数えていくと最大の計算量は$O(K|S|)$となり間に合いません．ほかの方法を考えます．
 `X`を連続で満たす最大の区間を求めると考えると**しゃくとり法**で解けると考えることができます．
　しゃくとり法については以下けんちょんさんの記事をおすすめです！

・[しゃくとり法 (尺取り法) の解説と、それを用いる問題のまとめ](https://qiita.com/drken/items/ecd1a472d3a0e7db8dce)

また，範囲内の`.`の数を数えるために**累積和**を用います．累積和を用いることにより$O(1)$で範囲内の`.`の数がわかります！
それでは実装していきましょう！

#実装
**しゃくとり法**では，左の文字から`.`の数が$K$個以下まで右の文字に進んでいきます．
　範囲内の`.`の数が$K$個より大きくなった場合は**while**を抜けて，その時の**文字列の長さ**を求めます．これをSの文字数だけ繰り返し，最大の文字列の数が答えとなります．
 範囲内の`.`の数を求めるときに**累積和**を使ってます．
###コード
コードは以下になります

```C++:
#include <bits/stdc++.h>
using namespace std;

int main(){
    //入力
    string S;
    cin >> S;
    int K;
    cin >> K;

    int N = S.size(); //文字列の長さ
    vector<int> sum(N+5);
    sum[0] = 0; 

    //累積和を数える
    for(int i = 0; i < N; i++){
        if(S[i] == '.') sum[i+1] = sum[i] + 1;
        else{
            sum[i+1] = sum[i];
        }
    }
    
    int ans = 0, r = 0;
    //尺取り法
    for(int l = 0; l < N; l++){
        while (r < N && sum[r+1] - sum[l] <= K)
        {
            r++;
        }
        ans = max(ans, r - l);
    }
    cout << ans << endl;
}
```
##おわりに
　今回解いた方法以外に**二分探索**でも解けます！　
茶diffの問題の解説を書いていこうと思いますのでこれからもお願いします！

#参考記事
・[しゃくとり法 (尺取り法) の解説と、それを用いる問題のまとめ](https://qiita.com/drken/items/ecd1a472d3a0e7db8dce)
・[累積和を何も考えずに書けるようにする！](https://qiita.com/drken/items/56a6b68edef8fc605821)
品質改善のために開発フローの整備からQA参入してみた

# 1.はじめに
みなさま、こんにちは。
昨年は以下の記事で、開発チームの実装遅延を改善するため、QAとして私が行った立ち回りについてご紹介しました。

https://qiita.com/20P/private/18c7cc0ba8a274bd0d10

現在担当しているスマホゲームの運営タイトルでも、開発チームの実装遅延や職種間の連携漏れによって障害が発生しており、日々障害削減に取り組んでおります。

今回も品質改善のためにどんな打ち手をとったのかを簡単にご紹介したいと思います。


# 2.担当プロダクトの状況

前回記事を執筆した際に担当していたタイトルと比較して、現在担当しているタイトルは開発チームの規模や組織成熟度、事業フェーズが異なっています。

- 運用期間：リリースから間もない
- 組織規模：前回担当タイトルの4倍
- 施策規模：新規仕様の施策リリース・機能アップデートが頻繁に行われる

品質面での違いは、新規機能実装・機能改修を行ったことによる既存仕様への考慮漏れで障害が多く発生している点です。
特に、仕様決定＆作業依頼タイミングが開発規模に対して遅延しており、職種間での連携・レビューの時間が十分に取られていないことが課題になっています。

#3.改善のためのアクション
打ち手の方向性としては以下2つです。

- 開発タスクを漏れなく進行したこと（特に「職種間と連携・レビューしたこと」）のエビデンス残し
- 進行予定の開発スケジュールと、開発規模から逆算した本来望ましいスケジュールとの差分を可視化し、実行予定スケジュールを見直さなければいけないのか・そのままの進行でいいのかの明確化

具体的には、施策毎に以下のような開発フローチェックシートを導入しました。
![Z4bTUhPHvxAvzaRRpzTFJYsm6Iwk5Bn8MG7PaMenNftGD2rBkBOuxk_DhHrmvBsoASg4QbRFVGzAa1z7ekIdKgqm3tKg2SuOPAyEJ5eDsn5X-VYyhDieJoxpaw0E.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553255/5b494214-e3e4-cb94-bbb1-02b70a2b3737.png)

実行の際のポイントとしては以下3つです。

- 経営層への事前コミュニケーション
- 専用チームの編成
- 経過観測

チェックシートの導入＆運用には開発チーム全体を巻き込む必要があったため、最初に部長・本部長へ、現状の品質状況を定量的に提示し、改善に向けた取り組み内容を握りました。
その際に、開発チームでも「品質改善大臣」という立て付けで、導入推進担当のプランナー・エンジニアを立ててもらうよう依頼し、開発チームの担当者とQAのチームを編成しました。

チェックシートの導入にあたっては、QAからいきなり開発全体に周知＆導入するのではなく、事前にプランナー班・エンジニア班それぞれで概要説明を行ってもらい、段階的に導入をしていきました。

チェックシート導入後も、毎週品質改善大臣MTGを実施し、チェックシート運用状況の観測をしています。
チェックシートの進行が滞っているものは施策担当者へ状況を確認し、開発進捗が遅れているものはプロジェクトリスクとして各職種のリーダー陣へ周知する流れで運用しています。

# 4.チェックシート導入による効果
まだ運用に乗り始めてから1ヶ月ほどですが、現状で出ている効果としては2つです。

1つ目は、開発チームの組織課題が可視化され、体制強化に繋がった点です。
導入後、機能アップデート施策でのチェックシートの運用が毎回滞っており、機能アップデートの頻度・規模に対して、アップデート担当のプランナー班の工数が不足している状況が顕在化しました。
事業計画に対して適切な開発体制になるようにプロデューサーへアラートし、来年から体制変更予定となっています。

2つ目は、クライアントの更新はせず、サーバーデータ更新のみを行う施策での「仕様考慮漏れ」による障害が減ったことです。
チェックシート導入以前は、既存の機能のみで仕様の実現が可能と判断された施策で本番障害が発生し、実は追加実装・機能改修が必要だったことが判明するケースがありました。
サーバーデータ更新系の施策を担当している班ではチェックシートの運用が比較的適切に行われており、図の#4にて「プランナー判断ではクライアントの更新が必要ない施策でも、エンジニアの仕様確認を通す」フローも設けたことで、いまのところ上記のような障害は発生しておりません。

# 5.まとめ
今回の取り組みで実感したことは以下2つです。

- 開発チーム全体を巻き込む際は、品質に対して意識の高いキーパーソンと密に連携できる座組みを最初の段階でを構築し、段階的に巻き込んでいく
- 開発チームの組織課題に対しては、最終的に開発チームの責任者へ改善を依頼する必要があるが、その際に納得してもらうためのエビデンスを準備することが重要

少しでも、同様な課題に直面されている方の参考になりましたら幸いです。
【ゲーム制作】世界観・設定作りが捗るサイト集

各サイト名をクリックすると該当ページに移動できます
# 世界構築
## [Azgaar's Fantasy Map Generator](https://azgaar.github.io/Fantasy-Map-Generator/)
- 架空の大陸をランダム生成するサイト（生成後、改変も可能）
- 国・文化・町なども生成してくれる
- 生成後は画像やファイルにエクスポートして保存できる
- 使い方
    - http://neochi0204.blog14.fc2.com/blog-entry-1670.html
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/938939/120ebc68-a90c-083c-3d25-c8faed9c673d.png)

## [Medieval Fantasy City Generator](https://watabou.itch.io/medieval-fantasy-city-generator)
- 中世の城塞都市をランダムに生成する
- 右クリックで設定メニューを表示する
- 街のレベルデザインのヒントに
- 使い方
    - https://gigazine.net/news/20180910-medieval-fantasy-city-generator/
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/938939/60ff554c-5623-fd55-c67d-c3d9182eea28.png)

## [Village Generator](https://watabou.itch.io/village-generator)
- 村ジェネレータ
- 右クリックで設定メニューを表示する
- 使い方
    - https://gigazine.net/news/20200804-village-generator/
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/938939/db557f22-32d4-b1bf-7605-c79f6ab229f6.png)

## [Inkarnate](https://inkarnate.com/maps/)
- ペイントソフトのようにブラシで地形を描けるマップ生成ツール
- 自動で地形の生成も可能
- 使い方
    - https://gigazine.net/news/20200824-inkarnate/
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/938939/39166134-5005-4a5c-5cde-4a2bb34cec8c.png)

- 様々なタイプのマップを作れる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/938939/702d106e-058c-dea3-7809-cd5c7067805a.png)![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/938939/a49ecd45-1dfd-4e99-c017-4e3b3619efc2.png)

# 名前生成
## [架空国名メーカー](https://makergenerator.blog.jp/archives/Country_name.html)
- 架空の国名を生成する
- 漢字のみの国名も生成できる
- 地名・宿屋・神社名が生成できる姉妹サイトもある

## [すごい名前生成器](https://namegen.jp/)
- 人名をランダムに生成する
- 国を選択し、国ごとにそれっぽい人名を生成できる

# キャラ・性格
## [キャラクター性格設定辞典](https://www.kogarashi.jp/0-material/fantasy/chara.html)
- 性格に関するキーワード（「神経質」など）が用意されている。
  - 基本性格100個＋追加志向100個の計200ものキーワードが用意されている。
- 診断メーカーverもある。（サイト下部にリンク記載）
- それぞれの性格を組み合わせるスクリプトを組んでも面白そう。

## [Waifu Labs](https://waifulabs.com)
- GAN（架空の画像を生成するAI）を使ってキャラを生成するサイト。
- Waifu（＝嫁キャラ）なだけあって、美少女しか作れない。
- 生成結果そのままでは扱いづらいが、キャラデザインの種にはなりそう。
- 使い方
    - https://gigazine.net/news/20190802-waifu-labs/
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/938939/76369f1e-2d89-ee26-f102-408f2ae76615.png)
あるエンジニアの成長、あるいは悩みや変化。


この記事は [クラウドワークス Advent Calendar 2021](https://qiita.com/advent-calendar/2021/crowdworks) の 17 日目の記事です。

今年の夏でソフトウェアエンジニアになって 5 年が経ちました。
この5年間で取り組む課題の内容が結構変わってきたなと思ったので、学びや悩み、変化を書いてみました。

何かの参考か、酒のつまみになれば。

## エンジニアになる前(大学生時代)

- 非情報系の大学生をする
- 大学の講義で [Processing](https://ja.wikipedia.org/wiki/Processing) を扱うが、よくわからず挫折する

## エンジニア歴 0 〜 1 年(インターン時代)

- Webマーケティングのベンチャーでインターンをする
- 時給は約 1,000 円
- Rails を使った社内向けツールなどを開発する
- 自分が作ったもの使って喜んでくれる快感を知る
- Progate を使って Ruby や Rails などの基礎を学ぶ
- わからないことだらけなので、[web に関することをとにかくあの手この手で学ぶ](https://qiita.com/t0yohei/items/aa0e38d0a96c44362551)

## エンジニア歴 1 〜 2 年(SIer時代)

- 新卒で SIer に入社する
- 年収は 300 万円台
- プロダクト開発の新卒研修を楽しむ
- C# で業務アプリケーション開発を行う
- ベンチャーの空気感や Ruby が恋しくなり転職を考え始める
- Rails の会社を中心に転職活動を始めるが、経験が足りず全て落ちる
- Rails を使った個人サービスを作るなどし、転職に再チャレンジする
- Webの事業会社(クラウドワークス)の内定を得て承諾する
- 転職前に偉い人から「3 年後に後悔するぞ」と言われる(後悔はしなかった)

## エンジニア歴 2 〜 3 年(クラウドワークス 1 年目)

- クラウドワークスに転職する
- 年収が 400 万円台に
- 開発ツールなどに全く馴染みがなくて、入社3ヶ月間はわからないことを倒したらまた新しいわからないことが現れた、を繰り返す
- 開発チームで一番技術力がないので、付いて行くためにがむしゃらに学ぶ
- JavaScript を初めてちゃんと触り、[フロントエンド開発も多少慣れてくる](https://qiita.com/t0yohei/items/74befa69a403d5f4858f)
- 設計については基本的にミドルやシニアのエンジニアに任せっきり
- とにかく開発経験が不足していると思い、知り合い経由で副業を始める
- 副業の時給は約 3,000 円
- 副業が私生活を圧伏するようになり、半年ほどで辞める
- 小さなタスクだとある程度自走できるようになってくる
 
## エンジニア歴 3 〜 4 年(クラウドワークス 2 年目)

- アーキテクチャ選択や設計に対して、いくつかの選択肢とそれぞれのトレードオフを考え始めるようになる
- Vue.js にハマり、チームに取り入れようとする
- シニアエンジニアの支援を受け、チームで Vue.js を使うようになる
- 以後会社での [Vue.js の普及を推進する](https://qiita.com/t0yohei/items/d516fefaaad69b4022ec)
- Vue.js ドキュメントの翻訳など、ちょっとした OSS 活動に手を出す
- チーム移動で機能開発のチームから保守系のチームに移る
- 保守に関して最初はあまり好ましく思っていなかったが、やってみるとリプレイスなどは結構好きな仕事だと知る
- 大学時代の友人に誘われ、再び副業を始める
- 内容は SaaS の新規サービスで、インフラ環境の構築や Rails + React の機能開発を行う
- 以前の反省を活かし、私生活を圧伏しないように稼働時間を自由にしてもらう
- 時給は約 4,000 円
- 与えられた課題に対して、基本的には 1 人で設計し実装できるようになってくる

## エンジニア歴 4 〜 5 年(クラウドワークス 3 年目)

- エンジニアとして一人前になったと思うようになる
- チーム運営についても興味を持つようになる
- チームのエンジニアリーダーが退職して、代わりにリーダー的な役割が期待されるようになる
- うまくこなせなくて鬱になりかける
- マネージャーに相談して、担う役割を減らしてもらいなんとか持ち直す
- チームのためにも、家族のためにも常に余裕を持つことを決める
- 余裕も持つようにした分、無理したチャレンジはしないようになり学習量が減る
- 無理せず学べる範囲を模索しながら、情報系社会人大学院の講義を受講してみたりする
- 情報工学の基礎を少しちゃんと学び、計算量などを意識できるようになる

## エンジニア歴 5年 〜 今(クラウドワークス 4 年目)
- 開発チームのエンジニアリーダーとして振る舞い始める
- 年収が 500 万円台に
- クラウドワークスのサービスや組織、ソースコードに関する知識が増えてきて、色々聞かれたり頼られるようになる
- 純粋な技術力だけじゃなくて、プロジェクトマネジメントやコミュニケーション能力との組み合わせで評価されていると理解する
- コロナ禍の運動不足もあり、体力が衰えてガンガン開発できなくなる
- インターネットに答えが転がっていない、組織や[プロダクト固有の課題](https://qiita.com/t0yohei/items/53941dac0d523066b62f)に取り組むことが増える
- ちょっと難しい課題にじっくり取り組むのも、味わい深くて嫌いじゃないと知る
- 取り組む課題に対して、時間さえあれば学習しながらなんとか解決できる気がしてくる
- 自分がいなくなっても回るチームを作ろうとし、いざ自分がいなくても回るのを目撃して少し寂しくなる
- 元いたチームから離れ、新チームの立ち上げに取り組む
- 今ここ

## おまけ: これからについて
新チームで取り組むことが、自分が今までやってこなかった領域になるかもしれず、色々学びながら頑張っていく予定です。
最近のちょっとした悩みはキャリアに関することで、技術に特化したキャリアを目指してはいないけれど、開発は続けていきたい場合、どういうキャリアパスがあるんだろうかということです。新チームで色々やって行く中で見えてきたら良いな。
おわり。
M5Stackでイチゴの自動水やり装置を作る

イチゴをベランダで育て始めたのですが、家を何日か空けると水やりができないのが心配になります。

https://twitter.com/ma38su/status/1454779949567471630

調べてみると、M5StackやArduino、ESP32などをつかった自動水やり機を試作されている前例がいろいろみつかったので、自分もことにしました。

- https://ogimotokin.hatenablog.com/entry/2018/08/16/081029
- https://deviceplus.jp/hobby/grow-plants-device-01/

差分は水やり対象がプランターであるということでしょうか。（M5関係ない。。。）
まだ完成はしてないので、追記前提です。すみません。

## 準備したもの

- Atom Motion https://www.switch-science.com/catalog/6993/
- M5Stack用 水分測定センサ付き給水ポンプユニット https://www.switch-science.com/catalog/6913/
- 塩ビのパイプ (ホームセンターで300円くらいだったかな。)
- ドリル (塩ビに穴あける用。https://www.tamiya.com/japan/products/74041/index.html)
- https://www.amazon.co.jp/gp/product/B07BD5F19L/ref=ppx_yo_dt_b_asin_title_o04_s00?ie=UTF8&psc=1

# 塩ビのパイプを加工する。

50cmの塩ビのパイプを準備して、5cm毎にドリルで穴をあけ、片側をキャップ、片側をL字のコネクタとして、プランターに設置してみます。サイズ的には大体ぴったりでした。

https://twitter.com/ma38su/status/1467105436197584897

当初、写真のようにパイプを二股させて2つのプランターを一つのポンプで水やりすることを考えていたのですが、やってみると二股だとポンプから供給できる水量も足りず、片側に偏ってしまいました。なので、少し高くつきますが、プランター毎にポンプを設置することにします。

https://twitter.com/ma38su/status/1467291452040712192

あとは屋外に設置するため、念のため防水のケースに入れて完成です。

![FGo5f5oVIAICUcR.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/56566/1280c628-6da3-60b5-1747-ef2651e931be.jpeg)

ちなみにケースはGroveを通すために穴をあける必要がありました。穴をあけた部分は、安定運用できるようになったところでホットボンドでシールすればいいかなーと思っています。

あとは暫定ですが以下のような運用を試行しています。

- 水分計の値は、Ambientに飛ばして監視しています。
  - 直接雨が当たらなくても、雨の日が多いと、意外と水やりしなくても乾燥してないことがわかったり。
- 少量多潅水がいいらしいので、水分計で40%くらいを切ったときに、60sポンプを起動させるようにしています。
- まだ試験運用中なので30分毎にセンサー計測して、あとはディープスリープするようにしています。電池あまり持ちません。

以上です。
CSSのレスポンシブ対応の書き方いつも忘れちゃうのでメモ。


CSS書くたびに

「あれ…？『max-width:1024px』ってどういう意味だっけ…？
ｶﾁｬｶﾁｬ（検索）…あぁ『1024pxより幅が狭い場合は適応』か。』

みたいな流れを繰り返しているので、メモしておく。

# PCを基準にコーディングする場合

``` CSS
body{
  background-color: whitesmoke;
}

/* PCだけに適応する */
@media only screen and (min-width: 1025px) {
  body{
    background-color: whitesmoke;
  }
}

/* タブレット用のブレークポイント */
@media only screen and (max-width: 1024px) {
  body{
    background-color: #c4ffcb;
  }
}

/* スマホ用のブレークポイント */
@media only screen and (max-width: 599px) {
  body{
    background-color: #8ddef7;
  }
}
```

# スマホを基準にコーディングする場合
``` CSS
body{
  background-color: #8ddef7;
}

/* タブレット用のブレークポイント */
@media only screen and (min-width: 600px) {
  body{
    background-color: #c4ffcb;
  }
}

/* PC用のブレークポイント */
@media only screen and (min-width: 1025px) {
  body{
    background-color: whitesmoke;
  }
}
```
そのままコピペしたら背景色がブレークポイントごとに変わるはず。