DXは現場にダッシュボードという理屈にみる精神論

悲しい。悲しい。悲しい。

中小企業xDXデジタル人材のマッチングイベントは、日本の製造業の将来への悲観と私がDX人材として参加したことへの罪悪感を感じるには十分な時間でした。

本来の意味でのDXは、人材不足や供給能力に依拠した経営判断を実現するための変革のはずです。具体的な経営判断はたとえば「従業員がX年後にX人減っても生産を落とさないようにせよ」とか「急増するXX需要でもシェアを維持できるように現状資産のまま生産を最低でもX%上げよ」「新規ビジネスのための人員をX年までにX人捻出せよ」であるはずです。それに伴う変革がたとえば「受注してから生産ラインに乗せるまでの間接工数をX日にする」であり「生産ラインの効率をXX%上げてX人削減する」です。

DXに必要なものは経営者の命令であり、それを解決するアルゴリズムを組織に実装することです。

逆に言えば、DXは、現場のデータリテラシーをあげたり、データを見える化して現場に掲げてみたり、部署間のシステムを統合して巨大システムを三年かけて構築してみたり、全社の業務システムを整理して最適化しようとしたり、データに基づいたミーティングを期待したり、AIにデータ食わせて相談してみたり、紙運用をデジタルに置き換えたり、データに基づいた経営判断を行うためのものではないです。 これらのほとんど「どうでもいい」です。工場の生産能力が上がって世界シェアを維持できる生産体制と組織設計があるならバックオフィスが紙でもいいです。立派なハンコを押してください。

冒頭イベントで素晴らしかったのは、上流の皆さんが各企業の理想の姿を整理し必要なスキルまで落とし込んだ構想3ヶ月の「A4一枚」のプリントをもとに、そのスキルを持った私たち「DX人材」がその穴埋めするために、これまた同フォーマットの「A4一枚」程度の提案と経歴を書いてエントリーするという、極めてシステマチックで単純で合理的な運用フローと、各企業毎に「できるだけ多くの方と対話してほしい」という前座と「エントリーはかならず弊社経由で」という締めの言葉が十分に訓練された司会によって計5分間アナウンスされる会議が、厳格な発言ルールのもと安全に進行したことでした。

そのA4一枚から、だいたい読み取れたことはこうです。

このものづくりの国ジャパンでは、国をあげてBIダッシュボードを製造現場に提供します。 いわゆる上流のコンサルは、いまだにハンコを使っているのですよというメーカー担当者の渾身の自虐ネタを華麗にツッコミをいれてみたりして、私達が「伴走支援」しますとボケの続きを「対話」します。「データドリブン経営」という世界標準を言って聞かせ、「データを活用」すれば「DX」できますと希望で落としてみせます。 そうして「見える化」ダッシュボードが「将来的」に課題を「具体化」できるのだとして、ベンダーへ各システムや自動化スクリプトを「全体把握」して「最適化」して「一元化」したシステムを注文します。 ベンダーはシステムを全体を俯瞰して最適化したいという「中・長期目標」を具体的に要件定義して「ロードマップ化」することを迫られ、現場の声を聞いてみたり、既存システムの現状を聞いてみたりします。どんなに頑張っても答えがないので、要件定義に6ヶ月かければ検討して最善を結論したことになるのでしょうか。ここでベンダーのみなさんは「顧客が本当に必要だったもの」という名言が胸の中でチクチクしたり、「要件定義が一番難しい」というアイロニーに感動して救われたりするのでしょうか。 いざ完成した「ダッシュボード」では、毎秒微妙に変化するマテリアルデザインのキレイなグラフと、赤と黒がローテーションする96ピクセルの特大フォントの数字が描画されているのでした。この動くホワイトボードは現場とってもっと効率よく生産しろというスローガンに成り代わり、いつしか神棚に置いたほうが良いという改善案の生産を実現します。今度はもっと「小さく始める」ことで「現場のハレーション」を減らせるに違いないと「仮説」をたてて、6ヶ月かけて1日に1回"しか"更新されないダッシュボードを開発して「全社員型DX」でデータ活用の素晴らしさが隅々まで伝わるか「検証」してみたりします。あるいは「データ人材」を育成すれば業務が改善できるとPythonを勉強させたりするのでしょうか。ある人は、データとAI活用しでみんなで話し合ったら営業成績の改善施策が生まれはずだと「AI搭載BIツール」を導入してみましたが、しばらくして「CIソリューション」到来の予感に震えたりしています。

データドリブン経営?第六感?

必要なことは来る人手不足に対策することです。人手不足でも生産が維持できることです。 逆算してなにが必要か結論することです。そうしたら社内のDX担当に命令してください。

その命令が具体的に何になるのか、プログラマの私には分かりません。 もっともよく知っているのはコンサルでも社内の労働者では無く、経営者のはずです。 他の人間が半年かけて集めたデータで専門家と市場分析した結論よりも、1週間で出した経営者の判断が覆ることはないはずです。

経営者判断のための完璧なデータは世の中に存在しないはずです。 「データドリブン経営」がほんとうに正でも、他人は絶対に上手くいく経営のためのデータを提示しません。DXについては「効率化がゴール」と無邪気にごまかされるか、具体的な手段に話をすり替わってしまうのが関の山です。

最後は勘しかないはずです。 第六感が完全に悪なら人間は機械に代替可能です。

労働者はそれが暗黙知である可能性に掛けます。 テクノロジーが進歩するまでは、ある地域の人たちは豚肉を食べないほうが良かったのです。

とにかく「XXでなければ会社は潰れる」を教えてください。 単純な話ではないかもしれませんが、複雑な事情は経営者以下が知る必要ないです。 もし他人の給与を知らなければ仕事をしない従業員がいれば、それは個別的な問題があるので話を聞いてあげてください。

経営問題がないなら何も変える必要はないです。皮肉ではなく、本当に。 私はそのくらいの危機感をもって、世の中のメーカーはDXを挑んでいるものだと想像していました。私は「人手不足」のニュースに踊らされていたのでしょうか?

どうするべきなのか?

上のような原理は極論でしょう。 いくつかメーカーに対してもう少しまともな提案を添えてエントリーしました。 とても迷いましたが、何もしないでポエム書くよりはマシでしょうか。

‘私が知恵を絞って実行するのだ’という決意です。

DXの精神主義的な傾向についてのお話でした。以上。

DX 

Goでの画像処理のパフォーマンスを改善した話 CPUキャッシュ、スライス

ウォーターマークをスクラッチでGoで実装した過程で、あまたの改善を行いましたが、その一つを紹介。 「画像のブロック処理のために、先んじて1次元スライスを並べ替える。」です。 メモリ効率は改善しましたが、速度については残念ながら期待する結果が得られませんでした。 Before After スライスを左上から行優先 スライスを左上からブロック/行優先 「ブロック」とは画像の分割したまとまりを指します。 例えば「4ピクセル四方のブロックに分割する」としたとき、80ピクセル四方の画像は、横に20個、縦に20個の計400個のブロックに分割できます。 ウォーターマークのアルゴリズムでは、画像の各ブロックに対してやや重たい計算します。 この時、データの順番を予め並び替えることで、CPUキャッシュを活用し、かつスライスのコピーをなくすことで、速度を改善を試みました。 実装詳細 もとのスライスから取得するためにコピーを複数回、かつ計算結果を戻すのに同じだけコピーを行います。 このコピーをしないように、取りやすい形にスライスを並べ替えておくのが今回の改善です。 src := []int{.....} // block 0番目を取りたい! // Before // src の 10 ~ 13, 24 ~ 27, xxx, xxx の4箇所からデータを取らないと行けない! copy(dist[0], src[0]) copy(dist[1], src[1]) copy(dist[2], src[2]) copy(dist[3], src[3]) // After // 先にシャッフル // 一度だけ参照すればOK! return src[0:8:8] 詳細は以下。 // Before wavelets := dwt.HaarDWT(img, srcWidth, nil) src := wavelets[0] // cA blockAt := 0 for startY := 0; startY < waveletHeight; startY += waveletBlockHeight { for startX := 0; startX < waveletWidth; startX += waveletBlockWidth { // ここでsrcからコピー b := get(src, startX, startY) // 計算: DCT -> SVD -> 計算 -> 逆変換で戻す v, idct := dct. [続きを読む]

slog.Handlerとログレベル

※追記 2025-10-26T21:09 stdout で詰まっている説。 SetDefaultの前後でログを出力していて、AWS Lambdaのデフォルトのレポート出力と競合している? デフォルトのレポート出力中に、SetDefaultがスイッチをしようとしてロック? 解決策 「関数の起動直後のSetDefaultをしてかついる場合は、ログを極力出さない」かもです。こんなんでいいのか、という気もします。 経過見て、余力あれば追記します。

以下、発生していた問題です。

独自に slog.Handler 実装して slog.HandlerOptions.Levelを指定すると、slog.SetDefaultした瞬間に動かなくなる。

// main.go
logger := slog.New(mylog.NewJSONHandler(os.Stdout, 
    &slog.HandlerOptions{Level: slog.LevelDebug},
))
slog.SetDefault(logger) // ここで死ぬ

// mylog.go

type mylogHandler struct {
	slog.Handler
}

func NewJSONHandler(w io.Writer, opts *slog.HandlerOptions) slog.Handler {
	return &mylogHandler{Handler: slog.NewJSONHandler(w, opts)}
}

※ AWS Lambda だと動かない問題が発生して、ローカルで動かすとなんの問題もない、という不思議状態。

問題点以上。上のコードに加えて、前後でslog.Debugなどを2,3コ出力していた。 以下、誤った解決策。

一応、下で解決した。

logger := slog.New(mylog.NewJSONHandler(os.Stdout, nil))
slog.SetDefault(logger)
slog.SetLogLoggerLevel(slog.LevelDebug)

// いける
// slog.HandlerOptions で渡さずに、 slog.SetLogLoggerLevel で指定する。

内部でデッドロックしているかもで、関連するISSUEもあるよう。 原因は正直不明で、気が向いたら調査します。 わからん。

go 

React.useStateは2種類ある

React.useStateにはある点で2種類あると考えている。 UI制御専用のもの そうでないもの これはUIコンポーネントに含められるかどうか、の違いがある。 UIコンポーネント UI制御専用のものとは。 テキストをマスクしたりしなかったりするコンポーネントを考える。(type=“password"は便宜上使用。) const [mask, setMask] = React.useState(false); return ( <> <input type={view ? "text" : "password"} /> <span onClick={() => setMask(!v => v)}>toggle</span> </> ) 入力された文字を表示するかどうかを、UIの責務の範囲内で完結されるべきと言えるときは、React.useStateが使って実装するのが良い(かもしれない)。 言い換えれば、UIコンポーネントとして表示/非表示を制御すると決めた場合、React.useStateを使っても問題はない。 強めに言えば、大体のUIコンポーネント内のReact.useStateは、表示/非表示を制御するのみと言っても良い。逆に言えば、それ以外のReact.useStateは、UIコンポーネントに含めるべきではないと考え始めると良いかもしれない。 UIコンポーネント外 先の例で言えば、input.valueに何を入力しているかは、UIコンポーネントの責務範囲外で、外部から受け入れるべき。 type Props = Pick<React.ComponentProps<"input">, "value">; function MaskedTextInput(props: Props) { } 便利なUIコンポーネントを作ろうとしてか、ビジネスロジックや他APIとの通信までコンポーネントに含まれてしまうパターンはよくありがちか。 設計方針次第ではあるが、ピュアなUIコンポーネントを作ろうと思ったときには、参考になるかもしれない。 例 いくつか例がある方がわかりやすいかもしれない。 React.useStateよりもコンポーネントの責務や命名のほうが主題かもしれない。 クリップボードにコピーするコンポーネント クリップボードに表示する値が動的に変化する場合は、それはおそらく呼び出し元で定義され流し込まれるべきかもしれない。 ここで利用したReact.useStateは、コピーしたらチェックマークを表示するようにするために利用されている。 function CopyInput(inputProps: React.ComponentProps<"input">) { const [copied, setCopied] = useState(false); const buttonProps = { onClick: () => { navigator. [続きを読む]

Go/iter でPipelineパターンやってみた

Goのv1.23で追加されたiterパッケージでPipelineパターンをやってみる。 Pipelineパターンはiterと併用できる? Pipelineパターンをiterと併用するメリットは? Pipelineパターンやiterはどこで使うべき? 要件 アプリケーションのバックエンド開発を想定。 商品を単価と個数をrepositoryから取り出して、単価x個数の合計金額をserviceで計算、最後handlerで計算結果を確認という流れ。 実装 iterパターンの実装を確認。 type Repository struct { items [][2]int // [][2]int{price, num} } func (r Repository) Generate() iter.Seq2[int, [2]int] { // r.itemsはコンストラクタによってlenは100 return slices.All(r.items) } type Service struct { r Repository } func (s Service) Iter() iter.Seq[int] { return func(yield func(int) bool) { for _, item := range s.r.Generate() { p := s.sumPrice(item[0], item[1]) _ = yield(p) } } } type Router struct { s Service } func (r Router) HandleWithIter() { var count int for sum := range r. [続きを読む]

ブログ始めました。

/img/top.jpg

ブログを始めました。目的は、これまでQiitaやnote、はてなブログに書いたものを集約することと、作ったものを紹介する記事を書くため。

フルリモートでエンジニアをやっています。

好きな言語はGo。

自己紹介は、こちらから。https://blob.yyyoichi.com/page/about/