OmniAICreator/Qiita-1.07M huggingface dataset:
【Java】「1つ以上のポートが無効」でTomcatが起動できない

# はじめに

EclipseからTomcatを起動しようとした時に、以下のようなエラーが発生して正常に起動できませんでした。
このエラーに思いのほか手こずったので、その時の対処を備忘録として記事にまとめてみました。

```
開始中localhostのTomcatv10.1サーバーに問題が発生しました。
1つ以上のポートが無効なため、サーバーを起動できません。
サーバー・エディターを開き、無効なポートを訂正してください。
```

![eclipse_error.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312166/70f7443d-006d-4ee8-4d8a-7f0ea5458e6e.png)

# エラーが起きた環境

* EclipseはTomcatが同梱されていないJava Standard版を利用したので、TomcatはEclipseとは別にインストールしました。
  * 今思えば、このチョイスが良くなかったのかもしれませんが...

|アプリ名など|バージョン|
|---|---|
|Java|Amazon Corretto 17 (.msi)|
|Eclipse|Eclipse 2022 (Pleiades All in One, Java Standard版)|
|Tomcat|Tomcat 10.1.17|

# 調査1（別のTomcatの停止）

* エラーメッセージの「1つ以上のポートが無効なため...」という文言を見て、他のアプリと8080番ポートが競合しているのではと考えました。
* 実はエラーの起きた環境にはTomcat9系もインストールされていたので、Tomcat9系が自動的に起動しないように［サービス］画面でTomcat9系を停止&無効化しましたが、状況は全く変わりませんでした。

# 調査2（ポートの確認）

* 次に、Tomcatが要求している8080番ポートを他のアプリが使っているかもしれないと考えて、 `netstat -a`とコマンドを入力してローカルの8080番ポートが使われていないかを確認しました。
* ところが8080番ポートは全く使われておらず、またしても空振りに...

# 調査3（Eclipse上の設定の確認）

* そこでEclipse上でのサーバー設定が間違っているのではと思い、サーバー設定を再確認することにしました。
* Eclipse上で［サーバー］ビューを開き、［サーバー］ビュー上でTomcat10.1をダブルクリックしてサーバーの設定画面を開きました。
* ここで［ポート］という設定項目を見ると、Tomcat管理ポートが`-`という無効値のような値が設定されていたため、8005番ポートを設定しました。
  * なぜこのような値が設定されていたのかは全く分かりませんが、同梱されているTomcatを使えばこのような事態にはならなかった可能性があります...
* 上記の設定を済ませてみると、Tomcatは無事に起動しました！！

![eclipse_tomcat.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/312166/9521ac1c-55c0-95e3-cad1-fb638245d086.png)

# 参考URL

* [サーバー・ビューの表示方法](https://foolprogrammer.blogspot.com/2012/06/blog-post.html)
エラー監視でシステムを安定化！緊急対応が減ってチームも安定化！

[人数半減の中で総開発量UPを実現させた秘訣 by エイチームコマーステック Advent Calendar 2023](https://qiita.com/advent-calendar/2023/ateam-commerce-tech)

7記事目は、エイチームコマーステック フロントエンドエンジニアの 大沢（@kazzzzzz） が担当します！

今回は、エラー監視に注力し運用を見直したことでさまざまな良いことがあったので、事例共有として記事にさせていただいております！エラー監視がうまく運用できていないと感じる方は特に見ていただきたいと思っております！

## エラー監視に注力した理由

エラー監視に注視し始めた頃、体制変更により私たちのエンジニアチームは半分の人数となりました。人数が減った分、エラー監視に注力することで、結果的に開発に割ける工数が増えると考えたためです。

エラーが発生した場合に迅速に対応すれば傷も浅く済みますし、ビジネスへの悪影響を最小限抑えるためのセーフティーネットとして存在するだけでもエンジニアの負担を下げることもできる考えました。

## Sentryとは？

以降、Sentryというツールに関する記載が多いので軽くどういうものかをご紹介しておきます。

Sentryはソフトウェアのエラー監視ツールで、発生したエラーをWebのダッシュボード一覧化したり、詳細ページでエラーメッセージ、スタックトレースなどをエラーごとに確認することができます。

他にも、個別エラーを無視設定や解決済みとすることで一覧から非表示にできたり、Slackなどのツールと連携することで新規エラーを通知させることもできます。

## これまでの運用と課題

エラー管理にSentry、エラー通知にSlackを使っています。

具体的な運用としては、

- 毎日の朝会で担当領域（フロントエンド・バックエンド）ごとに集まり、過去1日間のエラーを確認する
- 気になるエラーがあれば調査
- 都度Slackで気になるエラー通知があれば確認

です。

上記運用をする中でエラー通知数が多かったので、すべてを見るのではなく比較的発生回数が多いエラーに絞ってみることが多かったです。

Sentryに表示されているエラーの中で問題になりそうなエラーはないか？という視点で確認しておりました。非常に多くのエラーの中から問題になりそうなものを抽出する作業となっていたため抜け漏れが発生しました。また新規のエラーが発生しても原因不明のものですと次のアクションを明確に決めることができず、放置してしまうこともよくありました。

## やったこと

Sentryで一覧に表示されるエラーは常に0件にすることをエンジニアチームの目標としました。

「Sentryにエラーが表示されている状態＝システム異常」と定義づけできれば、システム異常か否かが非常にわかりやすくなります。その結果対応が必要なエラーを見逃してしまうといったような、システム異常か否かの判断ミスを防ぐことができると考えました。

この状態を目指すために下記のアクションを実施しました

### 毎日エラーをエンジニア全員で見る

まずは全員がエラーを認知することで、異変を見逃さずに検知できるようにしました。

それだけではなくすべてのエラーを確認することにしました。すべて見るのは大変ですが、大変だからこそ早くエラー一覧を少なくしたいという心理が働くと考えました。

### 放置しない運用

朝会でのエラーチェック時に、エラーの対処もしくは調査が必要な場合はその場でアサインを振り、対応不要の場合は無視設定を行うようにしました。

アサインを振ることで宙にも浮かなくなり、責任の所在が不明のエラーがなくなりました。

## ぶつかった課題と解決策

上記のやったことを遂行しましたが、思うようにいかない部分が出てきました。

それらの課題と解決策を説明します。

### やむを得ないエラーの取り扱い

お客さんのブラウザ拡張機能やボットなどのエラーは、通知されたところで基本的には対処のしようがありません。結局そのようなエラーが多く発生してしまい、エラーの確認作業を阻害してしまうということが起きてしまいました。

Sentryの設定にてブラウザ拡張機能や外部スクリプトのエラーを除去するような機能を有効にしたり、古すぎるブラウザは通知が来ないような設定をしました。

### 誤ったグルーピングがされたエラー

Sentryが意図しないエラーのグルーピングを行なってしまうことがありました。

例えば、同様のエラーにもかかわらず別々のエラーとしてグルーピングされなかったり、逆に別々のエラーなのに同様のエラーと判定されることがありました。前者のように別々のエラーとされてしまうと、一覧に表示されるエラーが必要以上に多くなってしまい確認の工数が多くかかりますし、逆に別の原因のエラーが同一エラーとグルーピングされてしまうと、リリースにより1つの原因をつぶしても解決済みにできないという課題がありました。

そこで、実装側でもエラーハンドリングの仕方を見直し、例えばGraphQLのクエリーごとにエラーがグルーピングされる様子するなどの対応をすることで、同じ原因のエラーが複数回通知されてしまうこと、違う原因のエラーが同様のグルーピングをされることを減らせました。

## 結果

エラー通知の信頼性が上がったことで、エラー通知に気づいてから修正が行われるまでのサイクルが早まりました。また、エンジニア全員がエラーについて把握できている状態が作れているため、例えば〇〇さんがいないからこのエラーは放置するといったこともなくなりました。

そして、想定外のことでしたが、エラー通知の確認作業に重点を置いたはずが、結果的に時間短縮になりました。
エラー通知の絶対数が徐々に減っていったため、確認すべき対象が減ったためです。

---

次回カレンダーは、

要件定義から設計で完全を目指さない！諦める勇気を持とう！

です！

もしご興味がありましたら次回記事もご購読いただければと思います！

よろしくお願い致します！
PowerAutomate: Power BI データセットのデータを CSV として保存する

# 背景

会社で 表題のことが出来るよーって話があったんですが
参考となる Office Scripts もなかったので、用意しておくか、と思ったというだけ

# 概要

1. Power BI に対して DAX Query でデータを取得 as JSON
1. JSON を Office Scripts で CSV へ変換
1. あとは保存するだけ

# フロー

フローは単純にこんな感じ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/c30076e7-b30d-fcd9-f061-785f3fe4f2d6.png)

## DAX Query でデータの取得

DAX Query は以前は面倒でしたけど、今は Power BI Desktop で DAX Query View を使えば簡単ですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/c5b6e932-a6dc-1e0c-e69d-de0ae8dc6116.png)

で、上手く動かせる Query を、Power Automate のアクションに貼り付けるだけ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/947189cc-62e8-1ad2-528a-d6f8f26a0ee7.png)


:::note
DAX Query View は、まだプレビュー機能なので、以下設定で有効化

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/a8214774-3c41-750b-3935-a79449efdea9.png)

:::

## Office Scripts で、JSON → CSV へ

- JSON から CSV は以下コードで実現

```typescript: ConvertJson2Csv

function main(
    workbook: ExcelScript.Workbook,
    rawJson: string
): string {
  let jsonRows: JSON[] = JSON.parse(rawJson);

  // ヘッダー行を作成
  let header = Object.keys(jsonRows[0]).join(", ");

  // データ行を作成
  let rows = jsonRows.map(obj => Object.values(obj).join(", "));

  // CSVデータを作成
  let csv = [header, ...rows].join("\r\n");

  // console.log(csv)

  return csv;
}
```

- あとは、JSONを渡して変換してやるだけ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/7935a766-94fe-921c-96cf-99b648bd0b6f.png)

## CSV 保存

特に苦も無く保存するだけですね

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/e4406c1d-a336-3671-6e7a-ae4bbb8879f4.png)


# あとがき

肝となるところは、以下2点でしたが・・
- DAX Query
- Office Scripts での変換

前者は Power BI の Dax Query View が、今どきのサンプル生成がいい感じ
後者も、サクッと終わった

ので・・拍子抜けするほど簡単ですね。
技術の進化は恐ろしい :laughing:
Javascript基礎学習④要素の取得・書換・追加・削除

勉強した事の復習！！


参考書籍：確かな力が身につくJavaScript「超」入門

## 要素を取得
・id属性から取得。
```
document.getElementById('id名')
```

・タグ名を持つ要素から取得。
※複数の要素にマッチした場合は、最初にマッチした要素1つだけを取得する。
```
document.querySelector("CSSセレクタ")
```
・タグ名を持つ複数の要素を取得したい場合。
```
document.querySelectorAll( CSSセレクタ )
```

## 要素の書換
・取得した要素の書換
```
document.getElementById("id名").textContent = 書き換えたい文字列; 
```

## 要素の追加
・取得した要素を追加
```
取得した要素.insertAdjacentHTML('挿入する場所','挿入する要素')
```
挿入する場所を指定するには、下記のキーワードを使用する。


|  キーワード| 挿入する場所  |
| ---- | ---- | 
|  beforebegin |  取得した要素の前に挿入（**開始タグの前**に挿入）  | 
|  afterbegin  |  取得した要素の子要素として挿入。すでに子要素がある場合はその前に挿入（**開始タグのすぐ後**に挿入）  | 
|  beforeend  |  取得した要素の子要素として挿入。すでに子要素がある場合はその後に挿入（**終了タグのすぐ前**に挿入）  | 
|  afterend  |  取得した要素の後に挿入（**終了タグの後**に挿入）  | 


## 要素の削除
・HTML要素の削除
```
document.getElemntById("id名").removeChild(削除する要素);
```
Cloud Identity登録

### 登録した理由
組織で利用できるGoogle Cloudの機能を試してみたかったため、Cloud Identity を個人で登録してみました。Cloud Identity は、Google が提供するクラウドベースの ID 管理サービスです。ユーザーのアカウント管理やアクセス制御、シングルサインオンなど、組織の IT セキュリティを強化するためのさまざまな機能を提供しています。

### Cloud Identity は無料で利用できる
Cloud Identity には、Free Edition と Premium Edition の 2 つのエディションがあります。Free Edition は、最大 50 人までのユーザーを登録でき、基本的な機能がすべて無料で利用できます。Premium Edition は、50 人以上のユーザーを登録したり、より高度な機能を利用したりしたい場合に利用できます。
詳細な違いはgoogleのサポートページで確認してください。
https://support.google.com/cloudidentity/answer/7431902?hl=ja

### 手順
Cloud Identity を登録するために次の手順を行いました。後から再現したため、一部の手順が不足しているかもしれません。

#### 1.ドメインを取得する
Cloud Idenityを利用するためにはドメインの所有が必要です。通常ドメインを取得するには年間幾らかのコストが必要ですが、個人で利用したいため、節約したいところです。ダイナミックDNSサービスを利用してサブドメインを無料で取得しました。
ダイナミックDNS（DDNS）はなんぞや？と思った方はググってください。

#### 2. Google Cloud Platformにログインし、「組織」メニューへ移動する
Google Cloud Platformコンソールにログインします。「組織」メニューへ移動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/8a0e8354-0055-0445-e619-fd4382ac65ca.png)

#### 3. Cloud Identity 申込み
「組織」メニューの「Cloud Identity に申し込む」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/520943e9-621a-12f5-845a-2ef63fd5b9f1.png)

自分でつかうので、「自分だけ」にして進めます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/59cd72a4-4ca5-1a1b-4f22-c6bf303a9ff4.png)

連絡先情報を入れて勧めます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/84a69e94-b75d-4a4e-7d85-c7c6dbf47a87.png)

#### 4. 所有ドメインの検証
ここで、事前に取得したサブドメインを入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/7ddb7957-1857-af1a-6eb4-703ab82fbe38.png)

Googleがドメインの所有権を確認するために、ドメインの DNS レコードを変更する必要があります。画面の指示に従って、取得したサブドメインの管理画面等から DNS レコードを変更します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/b6b034ad-5318-3d45-8e0d-1cc99daa98c2.png)



#### 5. Google Workspace管理コンソール
ドメインを所有していることがGoogleによって検証されると、Google Workspace管理コンソールに誘導されて、組織の最初のユーザーを作成するように促されます。

下記は作成した後の画面です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/4de6eb75-b386-f7a2-f177-a73f5ca28eb3.png)

#### 6. 組織の作成完了
これで組織を利用できるようになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/baf37879-b629-ab64-0162-f0a4938e6428.png)

### まとめ
Cloud Identityの無料版を使って、個人でもGoogle Cloudで組織の機能を試すことができます。
dockerで「failed to solve: rpc error: code = Unknown desc = failed to solve with frontend dockerfile.v0: failed to create LLB definition: dockerfile parse error line 6: unknown instruction: MKDIR」

macOS(sonoma) + dockerで、
「docker compose build」をすると、下記エラーが発生。

**failed to solve: rpc error: code = Unknown desc = failed to solve with frontend dockerfile.v0: failed to create LLB definition: dockerfile parse error line 6: unknown instruction: MKDIR**


結論、原因はdocker desktopのバージョンが古いのが原因のようでした。
僕の場合、アップデート後無事エラーが改善されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37406/2ee965fd-ec83-3f84-bf21-d1f64786958a.png)



## 環境

macOS sonoma(14.1.1)
docker desktop 4.26.0 (130397) ※アプデ前のバージョンは忘れました。。汗おそらく4.1x〜
Goにおけるインターフェースとは

## インターフェースとは
RubyやPythonには明示的に登場する概念ではないため、この場で確認しておきます。
Goにおけるインターフェースとは、メソッドの集合体のこと。他の言語では抽象クラスと呼ばれることもある概念です。

## インターフェースの例
```golang
type Shape interface {
    Area() float64 // 面積
    Perimeter() float64 // 周囲
}
```

`Shape`というインターフェースは`Area()`と`Perimeter()`という浮動小数点を返すメソッドを持っています。
このインターフェースはメソッドの詳細を知りません。

次に、このインターフェースを実装した`Rectangle`という構造体を定義します。
```go
type Rectangle struct {
    Width, Height float64
}

func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

func (
r Rectangle) Perimeter() float64 {
    return 2 * (r.Width + r.Height)
}
```
`Rectangle`は`Area()`と`Perimeter()`を持っているため、インターフェースを満たしています。

もう一つ`Circle`を定義してみます。

```go
type Circle struct {
	radius float64
}

func (c Circle) Area() float64 {
	return 3.14 * c.radius * c.radius
}

func (c Circle) Perimeter() float64 {
	return 2 * 3.14 * c.radius
}
```

呼出部分です。
```go
func main() {
	var s Shape
	s = Rectangle{width: 10, height: 20}
	fmt.Println(s.Area())
	fmt.Println(s.Perimeter())

	s = Circle{radius: 10}
	fmt.Println(s.Area())
	fmt.Println(s.Perimeter())
}
```

## これの何が嬉しいのか
例えば`PrintAreaAndPerimeter()`のような関数を定義してみましょう。
引数にはインターフェースを受け取ります。
```go
func PrintAreaAndPerimeter(s Shape) {
	fmt.Println(s.Area())
	fmt.Println(s.Perimeter())
}
```

先程の呼出部分を修正します。
```go
func main() {
	var r Shape
    var c Shape
	r = Rectangle{width: 10, height: 20}
	c = Circle{radius: 10}
    PrintAreaAndPerimeter(r)
	PrintAreaAndPerimeter(c)
}
```

このようにすることで、新たに構造体`Triangle`を定義するときでも、既存部分を修正する必要なく追加することが可能になります。
Macでスクリーンショットを撮ると右下に表示されるプレビューの表示時間を増やす


Mac でスクリーンショットを撮ると、右下にプレビュー（Floating Thumbnail）が表示されます。

こんな感じです↓

![スクリーンショット 2023-12-14 22.17.06.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227791/08c77dfc-50fd-e03e-5546-863148c79fb5.jpeg)

右下のプレビューをそのままドラッグ&ドロップもできるので、記事を書いたりする際はとても重宝します。

![スクリーンショット 2023-12-14 22.22.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227791/788ba8bf-b09e-c492-ce1c-2683b452d3fd.png)

しかし、このプレビューは **5 秒しか表示されません**。

ドラッグ&ドロップするためのウィンドウを準備している間に消えてしまって、Finder からスクリーンショットのディレクトリを開き直しに行って、とても多くの時間を無駄にしました。



## プレビューの表示時間を設定するコマンド

プレビューの表示時間はデフォルトで **5 秒**ですが、次のコマンドを実行することで任意の時間に変更できます。

```sh
$ defaults write com.apple.screencaptureui "thumbnailExpiration" -float 30 && killall SystemUIServe
```

この例では `30` 秒に設定していますが、任意の秒数に変更が可能です。

コマンドを実行すると即時反映されます。



## プレビューを非表示にする方法

プレビューはデフォルトで表示されますが、非表示にすることもできます。

まず、「QuickTime Player」アプリを開きます。

![スクリーンショット 2023-12-14 22.40.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227791/40fb6c2d-d49f-a594-fef5-dbce6648126b.png)

それから、「ファイル」>「新規画面収録」を選択します。

![スクリーンショット 2023-12-14 22.42.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227791/4702f923-a2a0-6af6-8b4d-193954aca9f2.png)

メニューバーの「オプション」>「フローティングサムネールを表示」のチェックマークを外すと、非表示にできます。

![スクリーンショット 2023-12-14 22.43.52.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227791/f1b82ea4-f457-a54f-8286-3b950b7cef41.jpeg)
SPFレコード設定の基本 – Googleを例にした実践ガイド

```
米国Google社が2024年2月以降、迷惑メール（なりすましメール）対策を強化した
「メール送信者のガイドライン(Email sender guidelines)」を適用すると発表しました。
```

1.  [SPFレコード設定の基本 – Googleを例にした実践ガイド](https://qiita.com/zhao-xy/items/749adb2c350f9bbf8440)
1. [Gmail（Google Workspace）とAWS Route 53を使用したDKIMレコード設定](https://qiita.com/zhao-xy/items/bb5bf364b4cb33aa98e6)
1. [AWS Route 53でのDMARCレコード設定ガイド](https://qiita.com/zhao-xy/items/5223a402a3d8c05b4ed1)

 ![unnamed.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155256/51fd5716-2a7d-0640-9dbf-cb556efc7646.png)
 
### はじめに

 インターネット上でのメール送信の信頼性を保つためには、SPF（Sender Policy Framework）レコードの設定が重要です。この記事では、Googleのメールサービスを例に、SPFレコードの設定方法とその重要性を説明します。

### SPFレコードとは？

SPFレコードは、DNS（Domain Name System）に設定されるテキストレコードの一種で、ドメインからの正当なメール送信元を定義します。これにより、ドメイン名を詐称したスパムやフィッシング攻撃を防ぐことが可能になります。

### 設定手順

1. DNS管理画面へのアクセス:
まず、ドメインのDNS設定ができる管理画面にログインします。

2. SPFレコードの追加:
    DNS設定において、新しいTXTレコードを追加します。

3. レコード値の設定:
    例えば、Googleのメールサービスを利用している場合は、以下のように設定します。

    ```
    v=spf1 include:_spf.google.com ~all
    ```

    この値は、Googleのサーバーから送信されるメールがこのドメインの正当なメールであることを示しています。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155256/b426be6e-40e4-2ad3-6e5c-eeac8e60f43f.png)


4. ポリシーの指定:
    SPFレコードの最後には、メール送信ポリシーを示す指示子を設定します。~all（SoftFail）は、SPFレコードにリストされていないサーバーからのメールを拒否するのではなく、疑わしいとマークすることを示します。

5. 重要なポイント

    > 正確な記述: SPFレコードは正確に記述する必要があります。間違った記述はメールの配信失敗につながります。
    複数のメールサービス: 複数のメールサービスを利用している場合、それぞれのサービスに対応するinclude指示子を追加する必要があります。
    文字制限: SPFレコードは255文字以内に収める必要があります。長すぎるレコードは分割するか、必要なサービスだけを含めるように調整します。

### 結論

SPFレコードの正確な設定は、ドメインの信頼性を高め、メールのセキュリティを保つ上で非常に重要です。特に、Googleのような大手サービスを使用する際には、これらの設定を見落とさないようにしましょう。SPFレコードの設定は、メールの安全な送受信とドメインの信頼性を確保するための第一歩です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155256/b021d0e8-d7e8-f91d-d01c-5f7f75e0b23f.png)


https://support.google.com/a/answer/81126?hl=en&fl=1&sjid=12825274696225656735-NC
Meta Quest 3で変わる、仕事の未来: Immersedでのスタンディングワークの新しい形

こちらは [ミライトデザイン Advent Calendar 2023](https://qiita.com/advent-calendar/2023/miraito-inc) の14日目の記事となっています。

13日目は [ブッチ](https://zenn.dev/t_kitamura) さんの 「[これからChatGPT（AI）と共存するために知っておくべきこと Part3](https://zenn.dev/t_kitamura/articles/8d3e8945d7fd35)」 でした。

実はこの記事はChatGPTを利用しつつ作成してたので、まさにタイムリーな感じでした。
生成APIの得意な領域等がまとめられている記事になっています。

# はじめに

1ヶ月程前の11月中旬に[Meta Quest3](https://www.meta.com/jp/quest/quest-3/)を購入しました。
実際に使って見てMeta Quest 3と[Immersed](https://immersed.com/)アプリを使用することで、働き方が大きく変わりそうな予感を感じました。

特に、スタンディングデスクがなくても立って作業が可能な点が、健康と生産性の両方を向上させる重要な特徴だと思っています。

# 使ってみて感じたこと

## 没入型体験と集中力の向上

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165884/d121be41-88cd-dc6c-433b-533141c6bb1c.png">

Meta Quest 3の没入型体験は、普段何気なく視覚に入ってくる情報を遮断してくれて集中力が高まります。

さらに、Immersedアプリと組み合わせることで、複数の仮想スクリーンを使用して効率的な作業環境を構築できます。

## 立っての作業による健康促進

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165884/fca6a90d-02b8-ed00-24e9-90d81d65f03a.png">

スタンディングデスクの代わりに、Meta Quest 3を使用して立って作業することは、健康促進に役に立ちそうです。

長時間の座り作業は健康リスクを高める可能性がありますが、VRを使用して立ちながら作業をすることで、これを軽減出来そうです。

物理ディスプレイを使ってスタンディングワーク環境を作るよりも気軽に実践出来る点が気に入りました。

## カスタマイズ可能な作業環境

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165884/e1fdfcbf-61d6-e874-35a8-974c4207e6e2.png">

Immersedを使用することで、VR上でのディスプレイ位置やサイズを自由に変更出来るので作業内容に応じて気軽にカスタマイズ出来ます。
また、パターンとして複数登録出来るので切り替えて使用することが可能です。

## 実践してみて気がついたTips

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165884/93b54860-0413-2815-b7db-7278b131d456.png">

実際にMeta Quest3で作業して気が付いたTip等を箇条書きでまとめます。

- 無線接続だと遅延で地味にストレスが溜まるので、有線接続がほぼ必須
- ハンドジェスチャー機能がONだと意図しないタイミングで検知されて邪魔になるのでOFFにした方が良い
- 基本的に物理モニタの数と配置をImmersed上でも揃えた方が混乱しない
- [BOBOVR](https://www.bobovr.com/collections/quest-3-accessories/products/bobovr-m3-pro)があると、フリップアップ(装着したままゴーグルを上に上げて外部を確認出来る状態にすること)が可能になるのであると便利
- フェイスカバーは外した方が顔への負担が無くて長時間装着しても疲れにくい


# さいごに

Meta Quest 3とImmersedの組み合わせは、立って作業する新しい形を提供してくれそうです。

このスタイルを実践してみてまだ日が浅いので、今後も色々と試行錯誤しながらVRでの作業環境を試してみようと思います。
VBAで別のブックの各シートの要素番号を取得する

## 現場で使うものなのであまり参考にしないでください
欠点としては、取得したいexcelブックを開いていないとエラー発火
開いていなくてもイベント発生時強制的に開くこともできるが、今回は割愛。
またハイパーリンクを使用していないので、ローカルでないと使用できない。

```
Sub shuukei()
    'シートネームを格納する配列を定義
    Dim sheetNames As Variant
    Dim i As Integer
    i = 0
    'フォーイーチ用変数
    Dim sheetName
    '各シートの最後の要素番号
    Dim arr() As Long
    '一時要素番号を格納する変数
    Dim selLength
    Dim ran As Range
    Dim num As Integer
    num = 0
    'ここで各シートの名前を配列に記述
    sheetNames = Array("Sheet1", "Sheet2", "Sheet3")
    '格納する配列の要素数を指定｡ここでは2
    ReDim arr(2)
    'エラーの際発火
    On Error GoTo errlabel
    
    For Each sheetName In sheetNames
        '現場では2マス先から入力しているので末尾に２を引く
        'text.xlsxと指定しているがここは取得したいブック名を指定
         selLength = Workbooks("test.xlsx").Worksheets(sheetName).Cells(Rows.Count, 2).End(xlUp).Row - 2
         arr(i) = selLength
         i = i + 1
    Next
    For Each ran In Range("C3:E3")
        ran.Value = arr(num)
        num = 1 + num
    Next
    
    Exit Sub
    'エラー処理
errlabel:
      Cells(1, 1).Value = "era"
End Sub

```
かなり限られたとこでしか使えないが、一応残しておきます。それでは！
The Creative Programmer

『The Creative Programmer』は、プログラマと創造性の関わりについて書かれた書籍です。〇〇プログラマというタイトルの本はたくさんあれど、久々にちょっと心を熱くさせる〇〇プログラマだと思いました。

<a href="https://www.amazon.co.jp/Creative-Programmer-Wouter-Groeneveld/dp/1633439054"><img src="https://m.media-amazon.com/images/I/61zsHxQPDIL._SL1500_.jpg" alt="The Creative Programmer" width="200"/></a>

ソフトウェアエンジニアの裾野が広がり、今ベテランエンジニアからよく聞くような問いが、本書の序文に書いてあります。

> なぜ新人は我々のフレームワークやベストプラクティスに追いつくことが多いのに、時には本当に重要なこと、つまりチームへの統合や問題解決を理解できないのか？明らかな技術的習熟度を超えて、本当に優れたプログラマーとは何か？

## 創造性を高めるための７つの要素

『The Creative Programmer』では、プログラマとして創造性を高めるために、７つの要素を掲げています。

* 技術的知識
* コラボレーションとコミュニケーション
* 制約
* 批判的思考
* 好奇心
* 創造的な心の状態
* 創造的技法

## 技術的な知識

『達人プログラマ』や『[RANGE(レンジ) 知識の「幅」が最強の武器になる](https://www.amazon.co.jp/gp/product/B0868DR365/)』に書いてあるように、専門領域だけを深めるよりも、それ以外のインプットも増やすことの重要性が説かれています。

インプットの手段も書籍に限らず、YouTubeを見たり、読書会に参加したり、Podcastを聴いたり多様化すると良いし、悩みを他人に相談するでも良い、としています。

「知っていること」が創造的なアイデアに向き合うのを潜在的に閉ざしてしまいがち、愚かにみえるアイデアを却下してしまいがち、になるので、知識の多様性はその面でも重要です。

## コミュニケーション

創造性を高めるコミュニケーション事例の研究成果が色々紹介されていますが、中でもエコーチェンバー効果は一番注意すべきポイントだと個人的には思いました。SNSなどが顕著ですが、自分の考えを強化する方向にしか働かないプラットフォームでしか活動していないと、どうしてもクリエイティビティが損なわれてしまいがちです。これは「技術的な知識」のところの入力を多様化しよう、ということも解決策になります。真の批判的なフィードバックを常に受け入れるようなチーム活動やコラボレーションができていなくてはなりません。

なお私個人としては、ChatGPTに自分のアイデアを渡して「このアイデアについて考えうる限りの批判的意見を出ししてくれ」と、批判生成マシンとして使っています。

## 制約

この分野ではお馴染みの横井軍平の「枯れた技術の水平思考」が引用されているのですが、課された制約が水平思考(ラテラルシンキング)を産み、創造的なアイデアを産むと言います。

役に立つ制約は、以下のように分類されます。

* 偶有的制約
    * 内在的制約: 選択した要素技術や環境に伴う制約
    * 課された制約: ステークホルダーから提示される制約
* 本質的制約
    * 自分で課した制約: 設計上のルールなど自分で自由度を律するための制約

※　偶有的/本質的の分類はアリストテレス由来だと思われます。

「自分で課した制約」はコントロール可能なので、これを使って創造性を意のままに高める、という話ではなく、やはりここでのポイントは「水平思考」のようです。

なお水平思考に関しては、以前渾身のまとめを書きましたので、そちらを参照ください。
* [アーキテクチャ設計における垂直思考と水平思考](https://scrapbox.io/kawasima/%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E8%A8%AD%E8%A8%88%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E5%9E%82%E7%9B%B4%E6%80%9D%E8%80%83%E3%81%A8%E6%B0%B4%E5%B9%B3%E6%80%9D%E8%80%83)

## まとめ

職業プログラマにとって、クリエイティビティがどれほど必要かは結局本書を読んでもわからないのですが、インプットの幅を広げるのは単純に楽しいし、水平思考も単純に楽しいのです。それでいいじゃないか、そういうプログラマが増えて欲しいという意志を『The Creative Programmer』から勝手に読み取りました。
機械学習エンジニア組織のマネジメントから得た学び

# はじめに
この記事は[Engineering Manager Advent Calendar 2023](https://qiita.com/advent-calendar/2023/em)の16日目の記事です。
ここでは機械学習エンジニア組織のマネジメントにおいて、筆者が体験した課題やそこで得た学びについて紹介します。一部、マネージャー就任以前のリーダーポジションだった頃の話も含まれます。

# 自己紹介
[エルピクセル株式会社](https://lpixel.net/?top)の研究開発本部でエンジニアリングマネージャー[^1]をしています。
自部門では医療分野の画像認識アルゴリズムの開発を担当していて、開発するアルゴリズムの大半は自社製品である画像診断支援AI EIRL(エイル)への搭載を目的としています。

# 自部門の特徴
画像認識アルゴリズムの開発を主務とする機械学習エンジニアのみで構成されています。
メンバーは前職が研究者、大企業の役員など多様性に満ちていて、それぞれの得意分野で高い能力を持つ一方で、企業におけるソフトウェアエンジニアとしての経験は少ないメンバーが多くを占めています。

# マネジメントから得た学び
## (1) タスクの粒度やスコープはメンバーに合わせて調整する
マネジメントを担当することになった当初は、WBSを作成し、ある程度均等な粒度に揃えたタスクをメンバーにアサインする手法を取りました。特に深い考えに基づくものではなく、前職での経験をそのまま当てはめてみただけだったのですが、結論としてこれは上手く機能しませんでした。

割り振ったタスクは一応消化されていくのですが、想定よりも進捗が悪く、タスクの難易度の想定を誤ったのか、スキル的なミスマッチがあったのかと色々悩みました。一方で「これはちょっと難しいかなぁ」と思うタスクを驚くほどのスピードで完了させることもあり、能力自体の問題でないことも分かりました。

こういった状況をしばらく続けて理解したことは、メンバー間で最適なタスク粒度・スコープの範囲に差異が大きいということです。あるメンバーは課題だけを説明して自由に解決手段を探索してもらうことが向いているし、別のあるメンバーは一度に担当するスコープをできるだけ最小化し、その範囲で最適な結果を追求してもらうことが向いているしと、個々のメンバーにとって最適な粒度・スコープでタスクをアサインすることで、能力を十全に発揮してもらえるということがこのときの学びでした。

## (2) スペシャリストには専門性の発揮に集中してもらう
自部門は高い専門性を持ったスペシャリスト系のメンバーが大半を占めているのですが、ジェネラリストではない故に得意分野と苦手分野がはっきりとしています。たとえばコーディングや統計的な分析には強みがある一方で個別のサンプルを定性分析することは苦手としていたり、既存手法の調査・応用を得意とする一方で自ら新規性の高い技術を開発することは苦手としていたりと、得意分野と苦手分野で成果の品質・速度に差がありました。

マネージャー就任当初は「そうは言っても、いつまでも苦手なままじゃ困るから得意じゃないことも頑張ろうね」というスタンスだったのですが、しばらくの期間様子を伺ってみた結果、スペシャリストにとって苦手な業務は本当に苦手そうだし、実際に効率も悪いという考えに変わりました。もちろん、本人に意欲があれば得意分野以外をチャレンジしてもらうことは経験の幅も広がり素晴らしいことなのですが、そうでなければ本人は苦痛だし、マネージャーとしては成果が得られないしということでお互いに不幸だなと今は考えています。

実際には程度問題でもあるので、対応可能な範囲は探っていく必要はありますが、基本のスタンスとしてはスペシャリストには専門性の発揮に集中してもらい、それ以外の部分は組織で補っていくことが、あるべき姿なのだろうなというのがここでの学びです。実現のためには各メンバーの特性を把握しておく必要があり、このためにも開発の様子をよく観察し、対話や議論などを介して理解を深めていくことが重要と考えています。


# おわりに
機械学習エンジニア組織のマネージャーとして体験した課題とそこから得た学びについて紹介させていただきました。
結論だけ見ると「当たり前じゃない？」と思うものになりましたが、意外と体感してみないと重要性は実感できないものだなと思っています。適切なマネジメント方法は組織やメンバーの性質によって変わりうるため、ここで紹介したことがあらゆる場合に当てはまるということはないと思いますが、類似の状況でマネジメントを行っている方や、これからマネージャーになろうとしている方の参考になれば幸いです。

[^1]:社内呼称はグループリーダー
【JavaScript/テスト】素のスクリプトを、GitHub Actionsでコードテストさせる

## はじめに
自分の環境を汚さずに、単体テストしたいですよね。

特に環境が重い重い`Node.js`アンチの僕は、ブラウザ用のためだけにJavaScriptライブラリを単体テストしたいと思っても、テストのためだけに`npm`なんたらかんたらで自分の環境を汚したくありません。

なので、`GitHub`に肩代わりしてもらいましょう。

## 手順

### 素ファイルをエクスポート
テストしたい素`.js`ファイルの末尾に、この2行を追加
```javascript
if (typeof module !== "undefined") 
    module.exports = { 関数あるいはクラス }
```
これは`Node.js`用の処理ですが、見ての通り`if`文で限定しているので、`Node.js`不使用時には影響を及ぼしません。

### テストコード
`Jest`を使います

テストコードのファイル名は
`(ファイル名).test.js`
にしてください

例です。
```javascript
//import
const {Quaternion} = require("./Quaternion.js")

test("constructor", () => {
	const q = new Quaternion(1, 2, 3, 4)
	expect(q.x).toBeCloseTo(1)
	expect(q.y).toBeCloseTo(2)
	expect(q.z).toBeCloseTo(3)
	expect(q.w).toBeCloseTo(4)
})

test("eulerAngles() zero-test", () => {
	const q = new Quaternion(0, 0, 0, 1)
	const e = q.eulerAngles
	expect(e[0]).toBeCloseTo(0)
	expect(e[1]).toBeCloseTo(0)
	expect(e[2]).toBeCloseTo(0)
})

test("eulerAngles()", () => {
	const q = new Quaternion(1, 2, 3, 4).normalized
	const e = q.eulerAngles
	expect(e[0]).toBeCloseTo(352.34, 2)
	expect(e[1]).toBeCloseTo(47.73, 2)
	expect(e[2]).toBeCloseTo(70.35, 2)
})
```

大事なのは冒頭、前項でエクスポートした関数ないしクラスをインポートします。

```javascript
const {クラス名, 関数名, ...} = require("jsファイルへのパス")
```

そして、テストしたいことを書きつづけてください。
```javascript
test("テスト名(結果で表示される)", () => {
	//テストする処理
})
```

テスト処理の書き方は[Jestドキュメンテーション](https://jestjs.io/docs/expect)参照で。

### Node.js設定
GitHubに読ませる`Node.js`の設定ファイルを書きこまねばなりません。
コピペで大丈夫ですし、書いてもNode.js未使用環境では仕様に影響ありません

```json:package.json
{
    "scripts": {
        "test": "jest"
    },
    "jest": {
        "testMatch": [
            "**/*.test.js"
        ]
    },
    "devDependencies": {
        "jest": "^24.8.0"
    }
}
```

この`package.json`をレポジトリ直下に設置

### GitHub Actionsの設定
(レポジトリ直下)/.github/workflowsディレクトリに、下記をコピペ。
```yaml:UnitTest.yml
name: unittest

on:
  pull_request:
    branches: [ main ]
  push:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Setup Nodejs
        uses: actions/setup-node@v1
        with:
          node-version: 16

      - name: Unit test
        run: |
          npm install
          npm test
```

## 結果
`main`ブランチにプルリクしたり直接pushすると、自動でGitHubがコードテストしてくれます

プルリク例
![IMG_2622.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2602537/aa9ea5d5-cbcf-3f99-021b-f61d227f7da7.jpeg)

コードテスト失敗時例
![IMG_2623.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2602537/c1ad9460-8a00-2f81-9b6f-60d3dfc92037.png)
ソフトウェアテストの７原則〜品質向上委員会の体験談〜 3レース目

## はじめに
私たちは2023年4月に株式会社ラクスパートナーズのQAエンジニアとして入社して、4人で活動しているチーム【**品質向上委員会**】です。
4月から3ヶ月間の研修を経て、現在は社内の月報管理システムの開発PJにQAチームとして参画しています。
私たちの活動を、JSTQB認定テスト技術者試験にでてきたテストの7原則に照らし合わせて4投稿のリレー方式で振り返ってみております。

今回は第3レース目！！
前回の投稿をご覧になっていない方は以下のリンクからぜひご覧ください！
[ソフトウェアテストの７原則〜品質向上委員会の体験談〜 2レース目](https://qiita.com/yuikuma0420/items/853997382b5ee5ded7a9)@yuikuma0420

## 原則５｜殺虫剤のパラドックスにご用心
>同じテストを何度も繰り返すと、最終的にはそのテストでは新しい欠陥を見つけられなくなる。この「殺虫剤のパラドックス」を回避するため、テストとテストデータを定期的に見直して、改定したり新規にテストを作成したりする必要がある（殺虫剤を繰り返し使用すると効果が低減するのと同様に、テストにおいても欠陥を見つける能力は低減する）。ただし、自動化されたリグレッションテストの場合は、同じテストを繰り返すことでリグレッションが低減しているという有益な結果を示すことができる。


### **＜経験から考える＞**

そもそも同じテストを何度も繰り返すのはどんな時でしょうか？
- システムのバージョンが変わった時
- 仕様が変わった時

このようなタイミングでのテストって、多くの場合でずっと同じ人もしくは、そのプロジェクトに長く携わっている人がテストを実施しますよね？
そうすると、、、
テストケースを更新しないでもシステムを知っているからこそなんとなくテストができちゃう

**ですが👀**
引き継ぎが発生した場合や、そのシステムの理解が浅い人がテストを実施する場合はどうでしょうか？
引き継がれたテスト実施者に十分な仕様理解の時間がないまま、リグレッションテストを実施する場合も考慮する必要があります。
新しい欠陥を見つけるどころか、テスト実施者がバグではないものをバグだと認識してしまうおそれがありますよね？
じゃあ何が大事なのか

### 〜 テストケースを最新時状態に保つこと 〜


特に、リグレッションテストのような繰り返し使うものに関しては最新状態に更新していくことが必要不可欠！
とはいえ、最新状態に保つのは難しいし、後回しにしがちだし、めんどくさいかもしれない。。。
ただ、QAとしてはとても大事なことだと思ってます！

最新状態に保たれているよと言い張れるためにも、QAエンジニアが**誰よりも仕様を把握しておくこと**も大事ですね🔥
  
　　
ちょっと横道
↓

:::note info
何度も繰り返されるようなテストの書き方について
-
文章途中でも書きましたが、ドキュメントを最新状態に保つことはコストがかなりかかるものかと思います。
だからこそ勇気を持って
- 冗長なテストを作成しないこと
- できるだけ少ないケースを作成することに挑戦する

そうすれば最新状態を保つコストは減るかもしれません。
:::


## 原則６｜ テストは状況次第
>状況が異なれば、テストの方法も変わる。例えば、安全性が重要な産業用制御ソフトウェアのテストは、e コマースモバイルアプリケーションのテストとは異なる。また、アジャイルプロジェクトとシーケンシャルソフトウェア開発ライフサイクルプロジェクトでは、テストの実行方法が異なる。

### **＜経験から考える＞**
「〇〇機能のテストをしてください」と言われたら、その機能のテストをするためにテストを作成していくと思います。
実際に私はただなんとなくテストを作成していました。
機能によってテストが違うだけで、状況によって変わるようなテストは作成していませんでした。
ただ、テストって粒度を細かくすれば無限にできますよね？
とはいえ、時間がないからこのぐらいでいいか？ともできないですよね。

大事なのは **テストをした先のことまで考えること**

自分たちが取り組んでみたのは
### 〜 リリース判定基準を設けること 〜

テストをしたその先にあるのはリリースであることが多かったです。
状況によってリリース判定基準は変わります。
リリース判定基準に沿ったテストを作ることが状況に合わせたテストなのかなと思いました！

判定基準を設けることで、
- テスト作成の粒度が揃う
- 冗長なテストがなくなる
- テスト作成前に基準を設ければ、テストしたからリリースできるよって言える。（実際には言えてない。）

なんとなくテストを作成、実施するのではなくて、**なんのためのテストなのかを考えること**が大事なのだと思いました🔥


## 最後に
私たちはテスターとしてではなくて、QAとして何ができるのか、何を意識しなくてはいけないのかを常に考えています！
思考を続けることがなんだかんだ大事なのかなと思ってます。
だからこそ、ご意見等あれば沢山いただきたいです！
最後まで読んでいただき、ありがとうございました！
次回の「品質向上委員会投稿リレー」は
12/17(日)に@j_haru さんが、原則7と⚪︎⚪︎についてお話しします
お楽しみに～
AWS Backint Agentの最新バージョンのインストール方法

# AWS Backint Agent紹介

SAP HANA向けのAWS Backint Agentは、Amazon EC2インスタンス上で実行され、SAP認定のバックアップおよび復元ソリューションを提供します。このエージェントはSAP HANAデータベースをAmazon S3にバックアップし、SAP HANA Cockpit、SAP HANA Studio、SQLコマンドなどのSAP管理ツールを使用してデータベースを復元します。

AWS Backint Agentは、Amazon S3へのSAP HANAデータベースおよびカタログの完全なバックアップ、増分、差分、およびログバックアップをサポートしています。エージェントはSAP HANAデータベースサーバー上で実行され、バックアップとカタログがSAP HANAデータベースからBackint Agentに転送されます。

使用されたAWSサービスに基づいてのみ料金が発生し、SAP HANA向けAWS Backint Agentはすべての商用リージョンで利用可能です。

# 環境準備
AWSの公式ベージ[こちら](https://docs.aws.amazon.com/sap/latest/sap-hana/aws-backint-agent-sap-hana.html#aws-backint-agent-operating-systems)により、以下の環境の準備が必要となります。

### AWS Backint agent がサポートされているOS：

- SUSE Linux Enterprise Server
- SUSE Linux Enterprise Server for SAP
- Red Hat Enterprise Linux for SAP

### AWS Backint agent がサポートされているデータベース：
- SAP HANA 1.0 SP12 (single node and multi node)
- SAP HANA 2.0 and later (single node and multi node)



# 手順
まず、１つのインスタンスを構築しました。
OS：SUSE Linux Enterprise Server

![手順](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/457891/ed182778-8900-a0ab-c742-6a44f4a22428.png)


S3を作成しました。
![S3を作成しました。](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/457891/1426e75a-11d2-0896-35a4-6f4795b098f8.png)

そして、EC2インスタンスにS3にアクセスできるようIAMロールを付けます。「S3FullAccess」権限を付けます。


# AWS Backint Agentのインストール/設定
AWS Backint Agentのインストール方法は3つあります。
今回はAWS Backint Installer方法でAWS Backint Agentをインストールします。

まずはOSにログインします。tmpに移動して、インストールのコマンドを実行します。

```
cd /tmp
sudo aws s3 cp s3://awssap-backint-agent/binary/latest/install-aws-backint-agent /tmp/
```

# 確認
最新版のAWS Backint Agentをインストールしました。
![最新版](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/457891/eff82a46-df84-cce9-7244-3856973693cf.png)
GitLab Container Registryの容量削減

# はじめに
* GitLab Community Editionで、自身でGitLabサーバーを立てて、GitLab Container Registryを利用している。
* 容量がひっ迫してきたので、GitLab Container Registryの容量削減を目的とする。
* 以下の方法を参考にしたが、導入が上手くいかなかかったので主に導入部分を記載する。

https://qiita.com/h-r-k-matsumoto/items/b852232cb228ceed536a

# 動作確認した環境
* GitLab CE ([15.8.3](https://hub.docker.com/layers/gitlab/gitlab-ce/15.8.3-ce.0/images/sha256-2b57f97d19e812735cbd32b97bcb7d42960a2ba0ae60ae776b59fea2ddabf845?context=explore))
  * Docker上で動作確認実施
 
# 方法

[上記の記事](https://qiita.com/h-r-k-matsumoto/items/b852232cb228ceed536a)のように、
  * [gitlab-ctl registry-garbage-collect](https://docs.gitlab.com/omnibus/maintenance/#container-registry-garbage-collection)では上手いかなかった（容量削減できなかった）。
  * [docker-distribution-pruner](https://gitlab.com/gitlab-org/docker-distribution-pruner)を使うと容量削減できた。

[docker-distribution-pruner](https://gitlab.com/gitlab-org/docker-distribution-pruner)の手順を以下に示す。

## 手順

1. (Dockerで構築している場合のみ)GitLab CEコンテナにアタッチ
2. docker-distribution-prunerがインストールされているか確認
    下記コマンドを実行して確認する（GitLab CEだと、docker-distribution-prunerは含まれている）。
    ```bash
    whereis docker-distribution-pruner
    ```
    下記のようになればOK。
    ```bash
    docker-distribution-pruner: /opt/gitlab/embedded/bin/docker-distribution-pruner
    ```
3. 削減できる容量の確認
    ```bash
    EXPERIMENTAL=true docker-distribution-pruner -config=/var/opt/gitlab/registry/config.yml -delete-old-tag-versions=true -soft-errors
    ```
    * オプション
      * -delete-old-tag-versions=true
        * 古いタグを消すかどうかのオプション (デフォルト値: true)
          * latestなど同じタグ名を使ってDockerイメージを多く作成している場合に、削減量が大きくなる
        * 公式ドキュメントではtrueを推奨 (削減量が大きくなる)   
        ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638977/7179d2e9-7b47-8f72-397d-395d67b6022d.png)
        https://gitlab.com/gitlab-org/docker-distribution-pruner/-/blob/master/README.md#options
 
    
    下記のような結果が出ることを確認する。
    ```bash
    WARN[XXXX] DELETEABLE INFO: XXXX links, XXXX blobs, 0 other, XX GB
    ```
4. 削除コマンドを実行 (実際にはdocker-backupフォルダに一旦、削除結果がバックアップされる)
    上記3.のコマンドに、```-delete```をつける。
    ```bash
    EXPERIMENTAL=true docker-distribution-pruner -config=/var/opt/gitlab/registry/config.yml -delete-old-tag-versions=true -soft-errors -delete
    ```
    ```/var/opt/gitlab/gitlab-rails/shared/registry/docker-backup```フォルダにバックアップファイルとして保存されることを確認する。

5. 完全削除
    バックアップフォルダごと削除する。
    ```bash
    cd /var/opt/gitlab/gitlab-rails/shared/registry
    rm -rf docker-backup
    ```

# まとめ

GitLab CEに付属している「docker-distribution-pruner」を使って、GitLab Container Registryの容量を削減できた。

# 参考

* https://qiita.com/h-r-k-matsumoto/items/b852232cb228ceed536a
* https://tmegos.hatenablog.jp/entry/gitlab-container-registry-docker-distribution-pruner
DL4J使ってみた（サンプル）

#0.前置き
javaで機械学習をしてみたくなった（pythonで2年くらい触っていた）
”DL4J”というものが提供されているので環境構築する
DL4J（https://mgubaidullin.github.io/deeplearning4j-docs/ja/quickstart#%E3%82%AF%E3%82%A4%E3%83%83%E3%82%AF%E3%82%B9%E3%82%BF%E3%83%BC%E3%83%88%E3%82%AC%E3%82%A4%E3%83%89）

要求事項
・java 1.7以上（64ビットのみ対応）
・Apache Maven
・IntelliJ IDEA または　Eclipse
・Git

#1.環境
Windows11・wsl2・ubuntu-20.04

##1.1.java確認
いつもの
```
$ sudo apt update 
$ java -version
openjdk version "11.0.7" 2020-04-14
OpenJDK Runtime Environment (build 11.0.7+10-post-Ubuntu-3ubuntu1)
OpenJDK 64-Bit Server VM (build 11.0.7+10-post-Ubuntu-3ubuntu1, mixed mode, sharing)
$javac -version
javac 11.0.7
```
javaは11.0.7なので大丈夫

##1.2.Maven
確認
```
$ mvn -version
$: command not found
```
mavenを入れるところから
```
$ sudo apt install maven
$ mvn -version
Apache Maven 3.6.3
```
##1.3.IntelliJ IDEA
JetBrains ToolBoxをインストール
https://www.jetbrains.com/ja-jp/toolbox-app/ 

ToolboxからIDEA（無償版）をインストール

##1.4. Git
```
$ git clone git://git.kernel.org/pub/scm/git/git.git
```

##2.DL4Jのサンプル実行
```
$ git clone https://github.com/deeplearning4j/dl4j-examples.git
$ cd dl4j-examples/dl4j-examples/
$ mvn clean install
```
ここまですれば後はIDEAでサンプルプロジェクトを起動すれば実行できる！

Eclipseで実行している記事もあったが、知識不足から再現はできなかった

今後は自前のプログラムでDL4Jを動かしたい

#参考
https://mgubaidullin.github.io/deeplearning4j-docs/ja/quickstart
https://qiita.com/tamura__246/items/3893ec292284c7128069
https://zenn.dev/yu1ro/articles/b50fd110b8da73
C/C++でユニットテストするときの未解決のシンボル解決どうするのか知りたい

# この記事のモチベーション

C/C++でのユニットテストを書くときテストに関係ないシンボルを解決しておかないとテスト実行できない。
これををみんなどう解決しているのか知りたい。

 テスト対象に関係なくても全部必要なのもリンクする手間をかけてユニットテストのバイナリを作成するMakefileやvcxprojを書いているのだろうか?
 
それとも、C/C++のリンクの仕組を利用して、ユニットテスト用のバイナリを動かすためにテストダブルの様にシンボル定義してユニットテストを動かしているのだろうか?

どちらも手間のかかるつらい作業だが、そんな作業が必要ないようなプロダクトコードを書いて解決しているのだろうか?

# 実行時に未解決シンボルのエラーが出たときの解決方法

## 1. がんばってリンクする

エラーが出たシンボルの実体が定義されているオブジェクトやライブラリをリンクする。それがまた未定義のシンボルを引き連れてきたら、それも解決するライブラリをリンクするということを全て解決するまで再帰的に行う。

## 2. 自分でソースコード内に未解決シンボルを定義する

未解決のシンボルを自分でテストコード内に定義する。本物の実体があるオブジェクトはリンクしないか、それよりも先にリンクするなどする。リンカの挙動に気を付ける。
   ユニットテストを記述するファイルの最初の方とか、未解決シンボル解決専用のソースに次のようなコードを書いてリンクする。
   
テストコードを実行したときに次のようにエラーが出て、
  ```bash
  undefined symbol: my_undefined_foo_symbol
  ```
nm で次のように出力されていたら
  ```bash
  U my_undefined_foo_symbol
  ```
ならば、ソースコードかデバッガーなどでシンボルの型や返り値と引数を調べて次のように定義する。

   ```C
   void my_undefined_symbol(){}

   // 以下ユニットテストコードが続く
   ```
そうすれば本来のシンボルが定義されているオブジェクトはリンクしなくてよい。

## 3. リンカーでシンボルを定義する

### 3.1 GCCの-defsymオプションを使う

GCCなら`-Wl,-defsym=my_undefined_symbol=0x0` と渡して解決する。
解決すべきシンボルが沢山あると書き切れずにつらい。

```bash
gcc ... -Wl,-defsym=my_undefined_symbol=0x0 ...
```

### 3.2 リンクスクリプトで定義してリンクする

ld で、シンボルを定義したファイルを作成しオブジェクトを作成する。

  ```ld:my_undefined_symbol.ld
  SECTIONS
  {
    . = 0x0;
    .text : { *(.text) }
    .data : { *(.data) }
    .bss : { *(.bss) }
    _my_undefined_symbol = 0x0;
  }
  ```
    
  ```bash:shell
  ld -T my_undefined_symbol.ld foo.o -o myunittest
  ```

のように書いて解決する。

## 4. テストする対象を絞る 

### 4.1 リンクの工夫

foo.c bar.c のうち bar.c が外部依存で未解決シンボルが付いてくる場合、foo.o だけをリンクしてユニットテストバイナリを作る。
bar.o は別の手で未解決シンボルを解決する。もしくはユニットテストはあきらめる。
foo.o のみリンクしたら bar.o をリンクしたときより解決すべきシンボルが減るならそれでこの方法を選択してもいい。
テスト存在しなかったモジュールで、変更を加えたときfoo.c だけ変更したのでテストはそこだけには追加するとかなどでも使う。bar.cを変更していないのにシンボル解決がんばりたくない。

```bash
gcc ... foo.o foo_test.o -o unittest
```

### 4.2 includeでプロダクトコードをinclude

includeでプロダクトコードをincludeしてビルドしてソースコードのテストをする。

```C
#include "../src/target.c"

// target.c 内のコードのテスト
```

## 5. プリプロセスで何とかする

ifdefやdefineなどマクロを駆使して未解決シンボルになる外部依存がユニットテストのときだけなくなるようにプロダクトコードを書き換える。
例えば #include "../src/foo.c" の直前にそのシンボルが呼ばれないようにするdefineを定義するなどしてごまかしを書ければ、プロダクトコードを書き換えなくて済むかもしれない。

ダミーのシンボル書いた方が簡単な気がする。
メンテナンスがダミーのシンボル定義書くのよりつらくなりそう。

## 6. もっと他の手段ある?

教えて😘

# ユニットテストバイナリを作成する以外のアプローチ

## a. リファクタする。

未解決シンボル少なくなるようにプロダクトコードをリファクタする。

## b. テストのアプローチを変える

無理にシンボル解決してまでユニットテストにこだわらないで、別のレベルのテストでテストを担保する。

## C. けど

けどユニットテスト書くときのシンボル解決の方法を知りたいのだ。



# 未解決シンボルの見つけ方

* ビルドして実行したときのエラーメッセージを見て、未解決シンボルがなくなるまで地道にシンボルを定義して解決していく。
* ```nm```や```dumpbin```を使用して未解決シンボルを見つけて実装する。
  * しかし本当にダミーのシンボル定義をしていい未解決シンボルか判断する一般的方法がない。

# 疑問

* みんな大量の未解決シンボルがあるとき、こんな地道な作業をしているのだろうか?
* 実はそんなに苦労せずに未解決シンボルを見つけてダミーの定義をする方法がないだろうか?
* そんな苦労しなくても普通に遅延束縛のようなアプローチがC/C++でもできるが私が知らないだけ?
* この方法で未解決シンボル解決しても、保守していく作業はどうしている?
* 入門用の記述だと未定義シンボル解決不要の内容しかないので実コードにあたるまで気付けない。
* 書籍もはっきり書いているものない気がする。リンクしたりテストダブルの記述の部分でみんな上のようなことを行えば未定義シンボルの解決できると気付くのだろうか?
* C++のカンファレンスでユニットテストのセッションわりとあってYouTubeに上がっているが、そういう未解決シンボル解決の話しているの見かけないので、みんなどうやってこういうアプローチ知るの?
* そういう未解決シンボルの解決方法を解説している書籍やブログを私が知らないだけ?
* 悩みはつきず、教えを乞いたい。
プロキシ環境でのホワイトリスト通信テストとテスト環境の構築

# 背景
セキュリティレベルの高い環境ではセキュリティの出口対策として外向けの通信を絞っている環境があることがあります。
しかし、近年のシステムはフロントエンド（Webやコマンドライン）側で様々なAPIと通信をするような実装がされていることが多いです。

# 目的
この記事ではコマンドラインを操作する際、どのエンドポイントと通信をしているのか調査し、そのエンドポイント（URL）のみ許可をして想定してる操作が可能か確認する通信テストの二つについてご紹介します。
（別途Webブラウザを操作する際の記事も書きたいと思います。）

# テスト環境の構築
今回はコマンドラインの通信を想定してテスト環境を構築します。
以下のような環境で、TestenvからはProxyを介してしかインターネットに出られない環境を構築します。
※ [サンプルリポジトリ](https://github.com/theoden9014/proxy-whitelist-tester)
![スクリーンショット 2023-12-15 21.48.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84972/fff190e0-4d95-4e10-a05d-2998900d5668.png)

この構成は、以下のようなdocker-composeで実現することができます。
プロキシ設定をProxyに向けることで実現しています。
```yaml
version: "3.8"
services:
  testenv:
    image: "amazonlinux:2023"
    environment:
      - "HTTP_PROXY=http://proxy:3128"
      - "HTTPS_PROXY=http://proxy:3128"
    networks:
      - "isolate"
  proxy:
    image: "ubuntu/squid"
    restart: "always"
    volumes:
      - "./squid.conf:/etc/squid/squid.conf"
      - "./whitelist:/etc/squid/whitelist"
    ports:
      - 3128
    networks:
      - "isolate"
      - "proxy"

networks:
  isolate:
    internal: true
  proxy:
```

# 通信調査方法
上記のテスト環境のTestenvのサーバーから確認したいコマンドを実行し、Proxyのアクセスログを確認することで、どういった通信が発生しているのか確認することができます。
サンプルリポジトリではホワイトリストにあるURL以外の通信以外は全て拒否するように設定しています。
サンプルリポジトリにあるsquid.confの以下の設定項目をコメントアウトすることで、全ての通信を許可するような設定になるので確認していくと良いと思います。
```
# ここまで一致しなかった場合は拒否
http_access deny all
```

# 通信テスト方法
通信に必要なエンドポイントがわかったら、whitelistファイルにURLを記述していきます。
その後、コメントアウトした設定をコメントインすることで、whitelistファイルにあるURL以外にはアクセスすることができなくなります。
その状態で、想定しているコマンドラインやプログラムなどを実行し、きちんと正常に動作するか確認することができます。
constevalの紹介

# はじめに
C++には`const` `constexpr` `consteval` `constinit` とかコンストがいっぱいあります。
今回ここでは、C++20から登場した `consteval`キーワードの使い方について紹介します。

# C++20 consteval関数 (即時関数)

C++11から使用できるconstexpr関数は実行時だけでなく、コンパイル時にも評価できる関数でした。
```cpp
constexpr int add(int a, int b)
{
    return a + b;
}
int main()
{
    constexpr int sumA = add(0, 1); // コンパイル時評価
    static_assert(sumA == 1);
    
    int sumB = add(0, 1);           // 実行時評価
}
```
これに対してconsteval関数はコンパイル時にのみ評価できます。
```cpp
consteval int add(int a, int b)
{
    return a + b;
}
int main()
{
    constexpr int sumA = add(0, 1); // コンパイル時評価
    static_assert(sumA == 1);

    // int sumB = add(0, 1);        // 実行時評価はコンパイルエラー
}
```
メタプログラミングで使う関数など、実行時に評価する想定のない物はconstevalにしておくほうが安全かもしれません

# C++23 if consteval

C++23から新しいconstevalの使い方が登場します。
それが if consteval です。

if constevalでは、コンパイル時評価されるときと実行時評価されるときで条件分岐することができます。

```cpp
constexpr int nahankanna()
{
    if consteval {
        // コンパイル時評価された場合はここを通る
        return 33;
    } else {
        // 実行時評価された場合はここを通る
        return 4;        
    }
}
int main()
{    
    constexpr int marines = nahankanna();
    int tigers = nahankanna();
    std::cout << marines << " - " << tigers << std::endl;
}
```

`if !consteval`と逆判定にする事も可能です。
```cpp
constexpr int nahankanna()
{
    if !consteval {
        // 実行時評価された場合はここを通る
        return 33;
    } else {
        // コンパイル時評価された場合はここを通る
        return 4;        
    }
}
```

:::note
C++20では `std::is_constant_evaluated()`関数を使って同様なことができました
```cpp
constexpr int nahankanna()
{
    if  (std::is_constant_evaluated()) {
        // コンパイル時評価された場合はここを通る
        return 33;
    } else {
        // 実行時評価された場合はここを通る
        return 4;        
    }
}
```
:::

:::note
またよく似たもので `if constexpr`がC++17から使用できます。
こちらは、コンパイル時に条件分岐する文法でした。

```cpp
template<bool isMarines>
constexpr int nahankanna()
{
    // 条件分岐をコンパイル時に行う
    if constexpr (isMarines) {
        return 33;
    } else {
        return 4;        
    }
}
```
:::

# まとめ

C++20から登場した`consteval`キーワードの使い方を紹介しました。

- consteval関数
- if consteval

なんでや！阪神関係ないやろ！
iTerm2 – Command+Enterで画面が最大化されるのを防ぐ設定 

- Settings (Preferenses) > Keys > KeyBindings
- Settings (Preferenses) > Profiles > Keys > KeyMappings

どちらかでCommand+Enterにignoreを設定する

 <img width="818" alt="image" src="https://github.com/YumaInaura/YumaInaura/assets/13635059/399effd0-de8a-4480-a5c8-6452049e1f8a">

<img width="1072" alt="image" src="https://github.com/YumaInaura/YumaInaura/assets/13635059/e1bf980e-85d2-482e-9169-511f63c1a694">



# iTerm2関連

https://qiita.com/YumaInaura/items/e242d1426756f4da1bab

https://qiita.com/YumaInaura/items/9039d6fc2c62ddc59b92


https://qiita.com/YumaInaura/items/e12d96ca8564875b93c4

https://qiita.com/YumaInaura/items/19b66d47d25ac0e1eee2

https://qiita.com/YumaInaura/items/dc7f409decd71ea93647


# チャットメンバー募集


何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ


# Twitter

https://twitter.com/YumaInaura
趣味開発のフォーマッタが業務でちょっと役立った話

これは[クラスター Advent Calendar 2023](https://qiita.com/advent-calendar/2023/cluster)（シリーズ2）の15日目の記事です。
昨日は @rizumi さんの「[2023年に実施したチーム施策を振り返る](https://note.com/rizumi_jp/n/nbf9ffb7f067c)」でした。

---

こんにちは、クラスターの分析チーム[^analytics]に所属している @kitta65 です。
この記事ではクラスターで行っている分析の話…ではなく、趣味で作ったコードフォーマッタの話をしようと思います（実際に作ったのが以下です）。

https://github.com/kitta65/lkmlfmt

最初にクラスターのデータ活用環境についても、少しだけ触れます。

[^analytics]: 分析チームの業務内容に興味のある方は、メンバーの登壇レポートをご覧ください（[メタバースのデータ分析とはなにをやっているのか](https://tech-blog.cluster.mu/entry/2023/10/24)）。

## クラスターのデータ活用環境

クラスターの分析用データは、一度BigQueryに集約されます。私たちのチームはそのデータに対してdbtというツールで必要な変換を施し、LookerというBIツールでKPIの集計・可視化などを行っています。

https://cloud.google.com/bigquery

https://github.com/dbt-labs/dbt-core

https://cloud.google.com/looker/docs


実はLookerで利用するLookMLという言語がやや曲者で、フォーマッタなど周辺のツール群がそこまで充実していません。需要がありそうだから作ってしまおうというのがこの記事の趣旨です。

## 実装
### code -> ast

LookMLはこんな見た目をしています。Qiitaのシンタックスハイライトが効かなくて悲しいです。

```lookml
# this is comment
view: view_name {
  derived_table: {
    sql: select column_name from table_name ;;
  }
  dimension: dimension_name {
    primary_key: yes
    sql: ${TABLE}.column_name ;;
  }
  measure: measure_name {
    type: count
  }
}
```

まずはこれを以下のように抽象構文木（AST）として整理します。

![ast](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540947/50c6e828-d9ed-fb30-02e2-a89e3824f897.jpeg)

自力で構文解析するのも楽しいです[^interpreter]が、parser generatorを使うと楽ができます。今回[lark](https://github.com/lark-parser/lark)というPython製のparser generatorを利用したら、30行程度の定義[^lkmlfmtlark]で上記のような抽象構文木が得られました。

[^interpreter]: 読んだのはだいぶ前ですが、[Go言語でつくるインタプリタ](https://www.oreilly.co.jp/books/9784873118222/)という本はゼロから構文解析器を作るので面白かったです。
[^lkmlfmtlark]: https://github.com/kitta65/lkmlfmt/blob/main/lkmlfmt/lkml.lark

フォーマッタ向けにparser generatorを選ぶ際は、コメントを扱いやすいかどうかに注意するとよさそうです。larkはコメントの内容や位置といった、ASTには直接含まれない情報にもアクセスできる点で便利でした。

### ast -> code

抽象構文木ができたら、改行やインデントを整えて文字列として出力するだけです。今回はシンプルにPythonの文字列操作だけで実装してしまいました。
ちゃんとやるなら、既存のフォーマッタのプラグインとして実装する[^plugin]方向性もありだったと思います。

[^plugin]: 例えばPrettierにはプラグインを作成するための親切な[チュートリアル](https://prettier.io/docs/en/plugins.html#tutorials)があります。今回はPythonで完結させたかったので、使いませんでしたが。

実装時にハマりがちだったのは冪等性の問題です。特に、複数回実行してもコメントの位置を変えないのが意外と難しくて、慎重にテストしています。

また、これはLookMLに特有の悩みですが、SQLやHTMLといった他言語がしばしば埋め込まれます。それらについては[sqlfmt](https://sqlfmt.com/)など他のフォーマッタを呼び出すよう設計しています。

## その後

実装完了してからしばらく寝かせていたのですが、せっかくだから最近チームの皆にも使ってもらうことにしました。Lookerの開発はなるべく専用のIDE上で完結させたいので少し悩んだ結果、GitHub Actionsを使って任意のタイミングでフォーマットを整えるPRを出す方法に落ち着きました（スクショのような感じ）。一か月ほど試しましたが、チームの反応は悪くないです。普段LookMLを書いているよという人は、よければ使ってみてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540947/9c69fe04-319e-3a4b-0050-a34e535e7a4b.png)

---

以上です。
明日は @Swanman さんの記事です。
ボトムアップによる開発生産性の向上

# はじめに

株式会社クライドでプロダクトマネージャーとエンジニアリングマネージャーを務めている陶山大輝です。この1年間、事業部の開発生産性向上に注力してきたので、そこで得られた知見をこの記事を通じて共有したいと思います。

また、[開発生産性の未来：世界と日本の最前線事例から培うFour Keys向上〜ハイブリッドカンファレンス〜](https://findy.connpass.com/event/298196/)に参加し、様々な取り組みを目にしました。ただ、**ボトムアップで開発組織全体の開発生産性の向上を目指す**ようなセッションが少ないと感じたため、その観点で記事を構成しました。

# 開発生産性の重要性と定義

開発生産性の向上は、単なる技術的な問題ではなく、**経営の観点**で重要な要素です。この点を理解することで、適切に開発生産性について考察し、改善を進めることができます。

また、この記事では、[開発生産性について議論する前に知っておきたいこと](https://qiita.com/hirokidaichi/items/53f0865398829bdebef1)を参考に、仕事量と期待付加価値と実現付加価値の3段階に開発生産性を分割して話を進めます。

### 参考記事

- [CTOの頭の中：技術を財務で表現する](https://note.com/singtacks/n/nb7a63ad40c17)
- [プロダクト拡大と開発生産性](https://note.com/y_matsuwitter/n/n536ce1970728)
- [開発生産性の多角的接点〜1,000名のクリエイター組織 × 開発生産性〜](https://speakerdeck.com/i35_267/multifaceted-touchpoints-of-development-productivity)

# ボトムアップでの開発生産性向上の利点と欠点

ボトムアップでの開発生産性向上とは、**1つの開発チームを起点としてプロダクト（事業）の開発生産性を向上させ、開発組織全体へと展開するようなアプローチのこと**です。最終的に開発組織全体の開発生産性を向上させ、開発組織が適切に会社へと貢献するような仕組みづくりを行うことを目的とします。

利点としては2つあります。1つ目は、現場の実情に即した改善を進められることです。現場を起点として改善が開始されるので、より的確な施策を実行できます。2つ目は、メンバーのエンゲージメントが向上することです。メンバーが向上を実感しながら改善が進むため、直接的にエンゲージメントが高まります。

ただ、欠点としても2つあります。1つ目は、開発組織全体との戦略がずれている可能性があることです。それぞれの施策が開発組織全体の戦略と厳密には一致しない可能性があります。2つ目は、トップダウンの場合と比べて調整が難しいことです。方向性の統一や施策のためのリソース調整などに多くの時間を要します。

# 3段階の開発生産性向上

早速、ボトムアップによる3段階の開発生産性向上について紹介します。

## 施策の実行

まず、**開発チーム内での施策の実行**が必要です。目的としては、**仕事量の生産性の向上**です。

この段階ではアジリティが求められるので、改善の効果が明確なKPIを設定して、できるだけ施策を打っていくことを重視します。特に、**開発プロセスのボトルネック**を見つけ出し、**リードタイムを短くすること**に注力すると良いと考えています。KPIとしては、PRのオープンからマージまでの時間やPR数がおすすめです。

この段階で得られる成果としては、目的に加え、**開発チーム外からの信頼の増加**になります。信頼を得ることで、施策の戦略的な実行を行うための交渉が容易になります。

### 実践

https://speakerdeck.com/daikisuyama/rebiyunojian-zhi-siniyorukai-fa-sheng-chan-xing-noxiang-shang

## 施策の戦略的な実行

次に、PMやデザイナーなどを含む**プロダクト開発チーム内での施策の実行**が必要です。目的としては、**期待付加価値の生産性向上**です。

この段階では、施策をただ実行するのではなく、戦略的に実行する必要があります。なぜなら、エンジニアと異なるバックグラウンドのメンバーを巻き込むことで不確実性が高くなり、**方向性が不明確になりやすいから**です。

また、先ほどの段階では開発プロセスに着目しましたが、戦略的に取り組む上では**組織設計**にも目を向ける必要があります。なぜなら、開発プロセスのボトルネックになりやすいのはコミュニケーションの部分であり、その基礎となるのは組織設計であるからです。

この段階で得られる成果としては、目的に加え、組織や開発プロセスを含めた**プロダクト開発の型**の創出になります。この型の再現性を高めることで、施策の開発組織全体への展開につなげることができます。

### 実践

2023/11からEMを兼任することとなったため、組織戦略や組織KPIを組み直し、施策を戦略的に実行する準備をしました。現在は、ICEスコアの導入による優先順位の決定の単純化やPRD策定フローの再設計などの期待付加価値向上のための施策を計画しています。所属チームのリソースが少ないこともあり、組織戦略の策定と組織施策の実行を全て自分に属人化させて高速な施策の実行をする予定です。

※ 2023/12に所属する開発チームが解散したため、このフェーズの改善は十分に実践できませんでした。

## 施策の展開

最後に、**施策の開発組織全体への展開**が必要です。目的としては、**開発組織全体での仕事量と期待付加価値の生産性向上**です。

この段階では、戦略的に実行した施策を**再現性を持って展開すること**が必要です。（逆）コンウェイの法則の通り、プロダクトの特性と組織の特性は連動するため、どの点に再現性があるのかを注意深く確認しながら、適用する必要があります。

また、開発組織全体に広めるという段階では、ボトムアップでのアプローチに限界があるため、CTOなどの経営層を巻き込んで**トップダウンでのアプローチ**が必要となります。

この段階で得られる成果としては、目的に加え、**組織としての持続可能性**を獲得できると期待されます。

### 実践

2023/07ごろから徐々に展開を進めています。主な取り組みとしては、他チームのコンサルと社内LTでの施策の共有です。コンサルは2,3チームのリーダーを対象に行っていますが、継続的な改善に繋がっているのは1チームのみで難しさを感じています。また、社内のLTも、共感を得られる程度で改善には繋げられていません。

※ 2023/12に所属する開発チームが解散したため、このフェーズの改善は十分に実践できませんでした。

# まとめ

この記事を通じ、ボトムアップによる3段階の開発生産性の向上について紹介しました。小さな改善をきっかけとして開発組織全体の変化まで繋がれば幸いです。
日本語プログラミング言語Mind 生誕38周年の歴史をふりかえる

# はじめに
日本語プログラミング言語Mind は今年2023年で生誕38周年です。Mindが生誕してから今日まで、どのような発展をたどったのか振り返ってみます。

https://qiita.com/advent-calendar/2023/mind

日本語プログラミング言語 Mind（マインド）Advent Calendar 2023 シリーズ2　 1日目の記事です。


# 日本語プログラミング言語Mindの歴史

| 登場時期 | バージョン・製品名・通称 |備考|
|:-------:|:-------:|:--------|
| 1985年|Mind version 1||
| 1986年|Mind for FM16β|富士通FM16β向け|
| 1986年|Mind for FM-R|富士通FM-R向け|
| 1990年|Mind WAVE|中学校向けMind|
| 1991年|Mind for O/2|IBM OS/2向け|
| 1992年|Mind for OASYS|富士通OASYS向け|
| 1994年|Mind version 5.2|NEC-9800向け|
| 1994年|Pure Mind PRO version 5.2|有償→無償|
| 1995年|Mind version 6 for UNIX|コードネームS6|
| 1996年|Mind version 7 for UNIX||
| 1997年|Mind version 7 for windows||
| 2012年|Mind for Android version 1 ||
| 2017年|Mind version 8 for windows|無償バージョン|
| 2017年|Mind version 8 for Linux|無償バージョン|
| 2019年|Mind for Android  version 2 ||


# おわりに

Mindの公式の歴史というのはみあたらないので、各種情報からまとめてみました。
Mind5.2の登場時期はMSAマニュアルの発行日より。
本家の@killyさんから編集リクエストあればよろこんで:santa_tone2:


https://www.scripts-lab.co.jp/corporate/history.html
既存のモデルにあった画像を中間テーブルを挟んで別テーブルにしたい時のオーバーライド

# はじめに
既存の画像データを新しいテーブル構造に移行する際、あまり変更を加えずにメソッドを扱えるようにデータの整合性を保ちつつ効率的に移行する方法を考えました。CarrierWaveを使用した画像の管理を例に、中間テーブルを介して画像データを別テーブルに移行する際のオーバーライドについて説明します。

## 背景
既存の Product モデルに直接関連付けられていた画像データを、新しい ProductImage モデルに移行することを例にし、画像の参照は中間テーブル ProductImageReference を介して行われるとして進めていきます。

## 実装ステップ
ImageUploader というCarrierWaveのアップローダーを ProductImage モデルにマウントされている状態にします。
``` ruby
class ProductImage < ApplicationRecord
  mount_uploader :image, ImageUploader
  # その他の設定
end
```
- オーバーライドの実装
Product モデル内で、画像データの参照方法を以下のようにオーバーライドしてみます
``` ruby
def image
  product_image&.image || super
end
```
上記のようにすると product_image は新しい画像モデルのインスタンスを参照し、存在する場合はその image を、そうでない場合は既存のメソッドを呼び出します。

# 結果
この方法により、新しい ProductImage モデルを用いて画像データを管理しつつ、既存の画像パスは保持されます。新しくアップロードされる画像は、カスタマイズされたパスに保存されます。
上記を使う場合は移行するタイミングが必要です。

# まとめ
本記事では、CarrierWaveを使用した画像データのオーバーライドと新旧のデータ構造の共存方法について考えました。

# 追記　※単一責任の原則について
単一責任の原則（Single Responsibility Principle, SRP）は、ソフトウェア設計の原則の一つで、「クラスは一つの理由だけで変更されるべきである」とされます。
この原則は、クラスやメソッドが一つの機能や責任だけを持つべきだという考えです。

## オーバーライドされた image メソッドの問題点
オーバーライドされた image メソッドは、二つの異なるソース（新しい ProductImage モデルと既存の画像）からデータを取得するという二つの責任を持っています。
このような場合、下記のような問題が想定されます。

- 保守性の低下：
メソッドが複数の責任を持つことで、そのメソッドのロジックが複雑になります。この場合では二つの異なるソースに対する変更を同時に考慮する必要が出てきます。
- 拡張性の問題
- 可読性の低下

（詳しく書くと長くなるので補ってください）