ESM アジャイル事業部 開発者ブログ

永和システムマネジメント アジャイル事業部の開発者ブログです。

RDBMS付属のツールで大規模データをインポートする

こんにちわ、swamp09です。

先日Railsプロジェクトで遭遇した大規模データのインポートについてお話しします。

ある外部システムから大規模なデータの連携をファイルで行うことになり、ファイルからの大規模データのインポートについていくつか方法を試しました。 大規模データは、月次で連携され毎回データをインポートします。 例として、その大規模データはユーザーデータで CSV 形式で受け取るとして、users.csv を毎月ファイルで受け取りインポートすると思ってください。

最初にやったのは、取り込む CSV ファイルを分割し並列で読み込む方法です。CSVファイルのサイズが1GB以上あり、データ数が100万件を超えていたので、ファイルを一度に読み込みデータをインポートするのはメモリへの負荷が大きすぎました。 ファイルの分割はsplitコマンドを使用し、10万件ずつのファイルに分割しました。 gem の Parallel を使用し、だいたい下記のような形になりました。 Parallel.map を使用するとデフォルトでCPUコア数から並列数を決定してくれます。https://github.com/grosser/parallel/blob/v1.20.1/lib/parallel/processor_count.rb#L8

def import(file_path)
  file_split(file_path)
  split_files = Dir.glob(SPLITTED_FILES_PATH + '*')
  
  Parallel.map(split_files) do |csv_file|
    csv = CSV.read(csv_file)
    User.insert_all(csv)
    File.delete(csv_file)
  end
end

def file_split(file_path)
 system("split -l 100000 -d --additional-suffix=.csv #{file_path} #{SPLITTED_FILES_PATH}splitted_", exception: true)
end

手元の開発環境で試して、約30分でインポートが終わりました。 こちらを試そうとしたのですが、実行する予定のサーバーのマシンパワーに余力がない懸念があり、もう少し良い方法がないか検討することにしました。

そんな時、チームメンバーから MySQL だとファイルの一括インポートできるツールがあるけど、それと似たようなツールあるだろうか、といった話があがりました。プロジェクトで使っていた RDBMS が Oracle だったのですが、調べてみると SQL*Loader というファイルからの一括インポートのツールがあったので試してみることにしました。 SQL*Loader では、制御ファイルを作ってインポートを行います。

users.csv のデータの例としてこのようなデータが入っているとします。

user_id name prefecture birth_day last_sign_in_at
1 田中 太郎 東京都 2000/06/01 2020/01/30
2 山田 次郎 神奈川県 2000/10/01 2020/03/30

上記のデータから、必要なものが user_idnameaddress だけであるとして、3つだけ抽出します。 制御ファイルを下記のように作成します。

LOAD DATA CHARACTERSET JA16SJIS
INFILE 'users.csv'
INTO TABLE users
APPEND
FIELDS TERMINATED BY ','
(
  user_id INTEGER EXTERNAL,
  name CHAR,
  prefecture CHAR,
  birth_day FILLER,
  last_sign_in_at FILLER
)

SQL*Loader で処理すると、ファイルの分割処理も必要なくなり、処理時間も格段に短くなりました。 スクリプトを自作していた時は約30分ぐらいかかっていた処理時間が SQL*Loader では約3分で終わったので、圧倒的スピードで終わるようになりました。

まとめ

Active Record を使って良い感じに速くデータをインポートする方法を考えなければ、と思っていたところRDBMSの付属のツールを使えば一気に解決しました。 他の RDBMS では、MySQL は mysqlimport があり、PostgreSQL では COPY があるようです。

大量のデータをファイルからインポートするケースはあまり多くないかもしれませんが、なにかの参考になれば幸いです。

Rails / OSS パッチ会オンライン 2021年5月のお知らせ

2021年5月の Rails / OSS パッチ会を 5月27日(木)にオンライン開催します。

この会をひとことでいうと、日頃のお仕事で使っている Rails をはじめとする OSS への upstream にパッチを送る会です。

会には Ruby と Rails のコミッターである顧問の a_matsuda もいますので、例えば Rails に送るパッチのネタがあるけれど、パッチを送るに適しているかの判断やパッチを送る流れが悩ましいときなど a_matsuda に相談して足がかりにするなどできます。

開催時間は 17:00-19:00 となりますがご都合のあう方はぜひご参加下さい。Zoom あたりのテレビ会議システムを使います。

当日の招待 URL は Idobata の esminc/rails ルームで共有する予定です。

idobata.io

特に募集ページなど設けませんが、上記理由から Idobata のアカウントが必要になると思います。

以下、前回の活動が関わる成果です。

koic: rubygems.org

github.com

osyo-manga: doctree

github.com

yahonda: ruby-plsql

github.com github.com github.com


開催日程の決まった RubyKaigi Takeout 2021 や、最近の Ruby / Rails まわりの動向に関する話題などあるかもしれません。

その他の開催方針については以下の Gist に記していますので、ご参照ください。

Reboot Rails/OSS meetup online · GitHub

浮動小数点数のバイナリ表現を10進数表記へ変換してみる

こんにちは、星野源です。すみません取り乱しました、はたけやまです。

最近は趣味でCPUを自作しています。自作のCPUを浮動小数点演算に対応させるためにオープンソースのFPUコア(浮動小数点演算装置)の使い方を調べていたのですが、FPUとやりとりするデータが浮動小数点数のビット列なので、返ってきた結果が正しいのか正しくないのかがパッと見で分からなくて生きるのが辛い...( 浮動小数点数の 0x3F800000 が10進数表記でいくつか分かります?答えは 1.0です)

そこで、浮動小数点数のビット列を10進数表記へ変換するスクリプトを書いてみました。

f:id:htkymtks:20210520234418p:plain (↑ FPUとのデータのやりとり)

浮動小数点数の構造

変換スクリプトを書く前に浮動小数点数について軽く説明を。「浮動小数点数」は小数点を含む数値をビット列にエンコードする表現方式で、現在では「IEEE 754」として標準化されたものが広く使われています。IEEE 754で定義されている浮動小数点数には32ビットの単精度浮動小数点数(single)や64ビットの倍精度浮動小数点数(double)などがあります。

32ビットの単精度浮動小数点数は以下のような内部構造を持ちます。

  • 符号部(sign)1ビット
  • 指数部(exponent)8ビット
  • 仮数部(fraction)23ビット

f:id:htkymtks:20210520235020p:plain ( File:Float example.svg - Wikimedia Commons )

手作業で浮動小数点数を求める

浮動小数点数への理解を深めるため、10進数の「-118.625」を例にして符号部、指数部、仮数部に入る値を計算してみます。

(1) 符号部

  • 符号部には、正の数の場合は0が、負の数の場合は1が入る
  • -118.625は負の数なので、「符号部 = 1」となる
  • (符号を求めた後は、-118.625 ではなく118.625 で後続の計算を行う)

(2) 10進数を2進数に変換する

  • 118.625を2進数に変換します
  • まずは整数部の118と小数部の0.625に分ける
  • 整数部
    • 118 = (64 * 1) + (32 * 1) + (16 * 1) + (8 * 0) + (4 * 1) + (2 * 1) + (1 * 0)
    • 10進数の「118」を2進数にすると「1110110」となる
    • 整数部の2進数表記 = 1110110
  • 小数部
    • 0.625 = (0.5 * 1) + (0.25 * 0) + (0.125 * 1)
    • 10進数の「0.625」を2進数にすると「0.101」となる
    • 小数部の2進数表記 = 0.101
  • 整数部 + 小数部
    • 1110110 + 0.101 = 1110110.101
  • 118.625の2進数表記 = 1110110.101

(3) 求めた2進数を指数表記へ変換

  • 1110110.101 = 1.110110101 * (2 ^ 6)
  • 「仮数 = 1.110110101」「指数 = 6」となる

(4) 指数部

(5) 仮数部

  • (3) で求めた仮数1.110110101のうち、仮数の先頭は常に「1.」となるので省略し、仮数部23ビットに足りない分はうしろに0を詰めて、「仮数部 = 11011010100000000000000」となる

(6) 符号 + 指数部 + 仮数部

  • 符号「1」+ 指数部「10000101」+ 仮数部「11011010100000000000000」
  • 「-118.625(10進数)」の32ビット単精度浮動小数点数の表記は「11000010111011010100000000000000」となる

以下のサイトを使って、計算した浮動小数点数の答え合わせしてみましょう。

手作業で求めた計算結果が「11000010111011010100000000000000」で、シミュレータで計算した結果が「11000010111011010100000000000000」。どうやら合ってそうです。

「浮動小数点数のバイナリ表現→10進数」変換スクリプト

32ビットの単精度浮動小数点数への理解が深まったので、さっそくスクリプトを書いてみます。

# bin2float.rb

# 32ビット単精度浮動小数点数の正式名称は「binary32」
class Binary32
  def initialize(bin32_str)
    # セパレータ `_` を取り除く
    str = bin32_str.gsub(/_/, '')

    if str.size == 8
      # 16進数表記の場合は2進数表記へ変換
      str = "%032b" % str.to_i(16)
    end

    raise 'Invalid Binary32 string' unless str.size == 32
    
    # 0バイト目は符号(+ or -)
    @sign = str[0]
    # 1〜8バイト目は指数部
    @exp = Exponent8.new(str[1..8])
    # 9〜32バイト目は仮数部
    @fract = Fraction23.new(str[9..])
  end

  def to_f
    # ゼロ
    return 0 if @exp.to_i == 0 && @fract.to_f == 0
    # 非正規数
    return Float::MIN if @exp.to_i == 0 && @fract.to_f != 0
    # 無限大
    return Float::INFINITY if @exp.to_i == 255 && @fract.to_f == 0
    # NaN
    return Float::NAN if @exp.to_i == 255 && @fract.to_f != 0

    # 符号が0ならプラスの値、1ならマイナス
    sign = @sign == "0" ? 1 : -1
    # 仮数部を求める際に省略した1.0を戻す
    fract = @fract.to_f + 1
    # 指数部を求める際に足したバイアス(127)を引く
    exp = @exp.to_i - 127
    
    sign * fract * (2 ** exp)
  end
end

# 指数部の8bit
class Exponent8
  def initialize(exp_str)
    @exp = exp_str
  end

  def to_i
    @exp.to_i(2)
  end
end

# 仮数部の23bit
class Fraction23
  def initialize(fract_str)
    @fract = fract_str
  end

  def to_f
    # (b0 * 0.5) + (b1 * 0.25) + (b2 * 0.125) + (b3 * 0.0625) + ...
    @fract.split(//).map(&:to_i).zip(1..23).map {|b, n|
      b * (2 ** (n * -1)).to_f
    }.sum
  end
end

p Binary32.new(ARGV[0]).to_f

使い方はこんな感じ。先ほど手で計算した「-118.625」も正しく計算できてそうです。

# 正の数
$ ruby bin2float.rb 01000010111011010100000000000000
118.625

# 負の数
$ ruby bin2float.rb 11000010111011010100000000000000  
-118.625

# ゼロ
$ ruby bin2float.rb 00000000000000000000000000000000
0

# 非正規化数(0ではないけれどBinary32で表現できないぐらい小さい値)
$ ruby bin2float.rb 00000000010000000000000000000000
2.2250738585072014e-308(Float::MINが返る)

# 無限大
$ ruby bin2float.rb 01111111100000000000000000000000
Infinity

# NaN
$ ruby bin2float.rb 01111111100000000000000000000001
NaN

# アンダーバーで区切ってもOK
$ ruby bin2float.rb 0_10000101_11011010100000000000000
118.625

# 16進数表記もOK
$ ruby bin2float.rb 42ED4000
118.625

突然のワンライナー

「わーい、できたー」と今回書いたスクリプトをTwitterへ投稿したところ id:udzura によりワンライナーに!!!

packとunpackに向き合ってみた

Rubyでバイナリアンを目指すためにpackとunpackに向き合った結果、僕にもワンライナーできたよー!!!ビッグエンディアンな単精度浮動小数点数としてunpackする必要があるのが罠でした。

gist1416977adf7d370eced64d22e0c273d2

参考

RuboCop オリジナル Cop 活用のオススメ

こんにちは、夜な夜なフォーチュンタワーに登っている nsgc です。

複数人でプロダクトやサービスを作る際に可読性や保守性を向上させるため、 コーディング規約を用意してフォーマットを統一したり、あきらかに不要な記述を静的解析ツールでチェックすることはよくあるかと思います。

Ruby でプログラミングしている場合、そういった Formatter/Linter としては RuboCop が有名ですが、 RuboCop 標準で用意されているルールだけではもの足りず、プロジェクトのコンテキストに特化した独自ルールが欲しい時がありませんか?

そんな時には、オリジナルの Cop の作成をオススメしたい!

tapp をチェックする Cop を作ってみよう

突然ですが tapp というツールをご存知でしょうか? tapp はメソッドチェーンの途中でもオブジェクトを表示できる優れものなのですが、プロダクトコードには入れたくありません。

コードレビューの時に「デバッグ用途の tapp が残ってますよ」 という指摘をしますが、本来はその前に CI で検知してほしいものです。そこで、今回 "tapp が呼ばれているか検知する" 独自 Cop を用意してみましょう。

まずは、Cop クラスの作成です。 RuboCop::Cop::Base を継承したクラスにコールバックを定義し、その中で問題になる条件がないかチェックし、最終的には警告メッセージを出すためのメソッドを呼びだします。

下記例では、メソッド呼び出し時に呼ばれる on_send コールバックを定義し、警告表示のための add_offense を呼んでいます。 RESTRICT_ON_SEND 定数に指定することで on_send で呼ばれる対象を tapp のみに限定し、add_offense で表示するメッセージを MSG 定数で設定しています。

# frozen_string_literal: true

module RuboCop
  module Cop
    module InternalAffairs
      #
      # # bad (ok during development)
      # # using tapp
      # def some_method
      #  do_something.tapp
      # end
      #
      # # good
      # def some_method
      #  do_something
      # end
      #
      class Tapp < Base
        RESTRICT_ON_SEND = %i(tapp).freeze
        MSG = 'Remove debugger'.freeze

        def on_send(node)
          add_offense(node)
        end
      end
    end
  end
end

Cop クラスが用意できたら、次にそのクラスを .rubocop.yml で読み込み、他の Cop 同様に有効にすると使えます。

require:
  - ./lib/rubocop/cop/internal_affairs/tapp

InternalAffairs/Tapp:
  Enabled: true

ここまで読んでどうでしょう?単純なメソッドコールの有り無し位ならとても簡単に作れそうですね。

もう少しリッチな構文チェックをしたい場合やオートコレクトを用意したい場合は公式で用意されている記事 が丁寧に書かれていますし、既存の Cop と近い Cop を作りたい場合は既にある実装 がリファレンスとして参考になります。

さいごに

私が参加しているプロジェクトでは、 特定のロールの利用者が操作時の証跡をデータベース上に残す必要があり、 controller でその証跡レコーディング用のメソッドが呼ばれているかを Cop でチェックしています。

また、Time::DATE_FORMATS で独自のフォーマットを用意しているのですが、それを用いないで書かれた場合にアラートをあげる Cop もいます。

チームのコードレビューで機械的に検出できるものを何度も指摘をしているなら、適応する Cop がないか探してみたり、なければ、自分たちで Cop を用意してチェックを任せ、人間にしかできないレビューに注力していきましょう!

価値創造契約10周年

アジャイル事業部で価値創造契約を担当している平田です。

価値創造契約という新しいサービスを発表して、また、最初のソフトウェアリリースから今年で10年になります。 価値創造契約についてはこちらから。(現在は新規受付は停止しています) https://agile.esm.co.jp/services/value_creating_contract/

詳細は上記のページに書いていますが、特徴を説明すると、初期費用0円とした上で、利用している期間だけ月額利用料をいただき、解約は自由というサービスモデルです。 これを発表した当時は、まだ今ほどアジャイル開発が受け入れられていなかった時期でもあり、「任せてくれたら大丈夫」という自信と覚悟を見せるためのご提案でした。 (そう思うと、請負契約ではなく、リスクをお客様側が持つ形での契約が一般的になっている現在は、隔世の感がありますね。)

価値創造契約はビジネスとして拡大はしなかったものの、ご契約いただいているシステムはいずれも長生きしており、継続的にメンテナンスをしながら使い続けていただいています。 一番長いものはリリースから10年になり、未だにリプレースすることもなく、運用を続けています。

ビジネスとして拡大しなかったのは、いくつも要因が重なったことが理由ですが、その理由のひとつに、我々が継続的に「保守」をやっていく体制をうまく作れなかったというものがあります。 通常、我々が取り組んでいるアジャイルなやり方では、ソフトウェアが動き続ける間、開発チームが継続的に追加開発や不具合修正を続けていきます。 一方、この価値創造契約のモデルでは、リリース後にメンテナンスは行うものの、開発チームを維持し続けるだけの費用をいただいていないため、チームは解散します。チームが解散してしまうことにより、知識の貯蔵庫が失われてしまうのが問題なのではないかと仮説を立てています。

今のところ、価値創造契約というサービスそのものを拡大していく予定はありませんが、上記のような課題を踏まえて、これまで通りの準委任契約での開発に引き続き取り組んでいくことはもちろんのこと、さらにその先をいくような契約形態やサービスを試行していきたいです。

ActiveRecord::LogSubscriber を使って追加でログを出力する

どうも muryoimpl です。

先日 ActiveRecord::LobSubscriber を使ってログ出力に手を加えたので、その意図と実装例をご紹介したいと思います。

ActiveRecord::LogSubscriber とは

ActiveRecord で発行されたクエリをログに出力する役割を担ったクラスです。ログレベルを debug とした場合に、ActiveRecord のクエリを発行した際に実行時間や SQL 文が出力されますが、それはこのクラスが活躍しているおかげです。

ActiveSupport::Notifications の仕組みを使ってログに記録する ActiveSupport::LogSubscriber を継承したクラスとなっていて、ActiveSupport::LogSubscriber の API ドキュメントをみると、ActiveRecord::LogSubscriber を使った例が記載されています。

ActiveRecord::LogSubscriber を継承した独自のクラスを作り、sql メソッドを上書き実装することで、既存のクエリのログ出力に加え、独自のログ出力を追加することができます。

なぜ ActiveRecord::LogSubscriber を使ったのか

今回私は、特定のテーブルのクエリが発行されているかどうか、発行されたときにどのくらいの時間がかかっているのかを確認したいがために、ActiveRecord::LogSubscriber を継承した独自クラスを作成しました。

Rails アプリケーションのログレベル全体を下げるとログの量が爆発的に増えてしまい、大量のSQLにより欲しい情報が埋もれてしまって探しにくくなってしまう問題があり、条件を指定してログ出力できないか?ということで、ActiveRecord::LogSubscriber の出番となったわけです。

この後記載する実装例でも出てきますが、SQL文やクエリに渡されたパラメータが payload として渡されてくるので、これらを条件にして出力する/しないの切り替えを柔軟に設定ができるのではないかと思います。

ActiveRecord::LogSubscriber 自体には、クエリが実装されているソースがどこかを出力するメソッドも実装されているので、あるテーブルへのクエリがどこから出力されるかを調べることもできるでしょう。

テーブルの特定の属性が更新された場合のみSQL文を出力する実装例

以下の環境で動作確認をしています。

  • Ruby on Rails: 6.1.3.1
  • Ruby: 2.7.3

ActiveRecord::LogSubscriber#sql の元の実装 や、ActiveRecord::LogSubscriber の API ドキュメント を参考にして実装しています。

今回は Profile モデルの memo 属性が更新された場合に、ログレベル info で SQL を出力するようにしています。ファイルは config/initializers 以下に配置しています。

class ProfileLogSubscriber < ActiveRecord::LogSubscriber
  def sql(event)
    self.class.runtime += event.duration
    payload = event.payload

    # debug レベルでログが二重に出力されないようにしている。
    # また、自分で実装したクエリ以外のSQLが出力対象とならないようにしている。
    return if logger.debug? || IGNORE_PAYLOAD_NAMES.include?(payload[:name])

    sql_str = payload[:sql]
    # profiles と memo が両方含まれているクエリのみを対象とする
    return if !(/profiles/i.match?(sql_str)) || !(/memo/i.match?(sql_str))

    name  = "#{payload[:name]} (#{event.duration.round(1)}ms)"
    name  = "CACHE #{name}" if payload[:cached]

    binds = type_casted_binds(payload[:type_casted_binds])
    # 親クラスの実装は debug で出力されるようになっているが
    # 今回は info で出力する
    info("#{name}  #{color(sql_str, sql_color(sql_str), true)}; #{binds}")
  end
end

# AcitveSupport::Notifications の"active_record" の namespace に
# ProfileLogSubscriber#sql を登録する
ProfileLogSubscriber.attach_to :active_record

event.payload は Hash で、実行されたSQL文(:sql)、SQLの種類を示す名前(:name)、SQL に bind する値(:bind_casted_binds) 等の情報が含まれています。これらを使って、出力するかどうかの判定と、ログに出力する文字列を作成しています。

ちなみに payload{:name] ですが、ActiveRecrod::Relation#explain を呼び出したときは "EXPLAIN"、テーブル情報等スキーマの情報を取得するSQLが発行された場合は "SCHEMA" になります。その他には、transaction を開始/終了した場合は "TRANSACTION"、定義したテーブルのレコードを操作した場合は "<モデル名> <操作名>" (例: Profile Load)になります。

以下は、前述のコードを config/initializers ディレクトリに配置し、development 環境のログレベルを info に設定した上で、bin/rails console を実行してクエリを発行した様子の画像です。profiles テーブルの memo を更新したときのみ SQL が出力されているのがわかると思います。

f:id:muryoimpl:20210426112809p:plain
rails console での実行例

さいごに

ActiveRecord::LogSubscriber を使って、従来のログ出力に加えて、特定のテーブルや属性に更新があったときのみログ出力する例を紹介しました。

特定の条件のときだけログを追加で出力したいといった場合に、Rails 本体のログ出力機能を書き換えることなく使えるため便利です。実行されたSQLの確認やチューニング、呼び出し元を特定する場面等で使えるので、こういう機能があったな、と覚えておくと活用できる場面に出会うかもしれません。

失敗するということはどのようなことか

突然ですが。 みなさんはソフトウェア開発でどれくらい失敗をしていますか?

失敗することについて、私見をまじえつつ考察してみたいと思います。

プロジェクトで失敗していますか?

仕事でソフトウェア開発をする場合、プロジェクトという単位で開発をすることが多いと思います。 プロジェクトは、その性質から基本的に一点物、その開発は常に初めて経験するものです。

初めてですから、成功も失敗もすべてを予想できるわけではありません。 解決したい領域が自分の不慣れなものであれば、どうすれば成功するのか予測することはいっそう困難なものになります。

これでは何かしら失敗をすることは約束されたようなものですから、心置きなく失敗してもよさそうなものです。

そもそも失敗とは?

しっぱい【失敗】

やりそこなうこと。 目的を果たせないこと。 予期した効果をあげられないこと。

三省堂 スーパー大辞林

プロジェクトで実現したかったことが、開発したソフトウェアで実現できなかったとき、失敗と言うことができそうです。 実現したかったことが実現できなかったのですから、これは本来はネガティブな結果です。

一方でよく耳にするように、失敗することでうまくいかない方法を見つけることができたのだから無駄ではない、という考え方があります。 この立場に立つと、うまくいかいないソフトウェアを開発するためにかけた資源をどう評価するかという問題は残るものの、失敗そのものは無駄だったというわけではありません。

はやく成功に到達するために、はやく失敗するというのは、変化の早い環境の中ではむしろ当然のものという理解も広まってきています。

失敗は、無理に取り除くのではなく、成功のための踏み台ととらえるのがよさそうです。

ただし、それとは別で区別したいものに、避けられたはずの誤りというものもあります。 思い違い、思い込み、確認不足によるミス。 これらはプロジェクトの性質によるものでなく、むしろプロジェクトへの取り組み方によるものでしょう。

失敗することとは別に、誤りをおかさない工夫はしておきたいものです。

いつ失敗するか?

失敗は避けるものでないとして。 では、失敗するならいつがよいのでしょうか?

これは先ほど棚上げした、失敗したことにかけた資源をどう評価するか、という話にもつながりそうです。

最初に考えられるのが、失敗が許される枠を用意して、その中で失敗をすること。 本番に影響を与えず失敗できるように、いわば練習期間を用意して、うまく行く方法やうまくいかない方法を見つけることに当てる。

わたし個人としては、これには異論もあるとは思いますが、自分の能力が資源であり能力を伸ばし効果的に発揮するという点で、プログラマはアスリートと変わらない部分があると考えています。

練習せずに本番にのぞむアスリートがいないように、プログラマにも結果の成否にとらわれない枠があってよいのではないか、と感じています。 個人が自分の能力を伸ばすことに努めるのはもちろんのこと、プロジェクトとしてもチームとしての能力を伸ばすための時間があってよいのかもしれません。

一方で。 できあがった時点では成功なのか失敗なのか、判断がつかないものもあります。 例えば、これはわたし自身が最近経験したことですが、よいユーザインタフェースに改善したはずが、逆にユーザに戸惑いを与え不必要な操作を増やしてしまう結果になってしまったものがありました。 実際にサービスを提供してみないと、何が正解なのか本当にはわからないものです。

ユーザに損害がおよぶ事態はさけなければなりませんが、時には実世界で成否をはかることも必要で、プロジェクトはそんな状況も加味しておくべきなのかも知れません。

うまく失敗できているか?

失敗とは何で、いつなら失敗できるか考えてみましたが、もう一つ他にも重要な要素がありそうです。 失敗することの得手不得手です。

正直なところ。 わたしは、失敗することが下手です。 失敗すれば凹むし、めげるし、ふさぎます。 不必要なほどダメージを受けます。 ダメージを受けるというよりも、自分自身にダメージを与えているというのが正解かもしれません。

失敗できる条件がプロジェクトに用意されているとしても、自分の気持ちとして失敗をゆるせるかはまた別の話。

感情は、本人以外が直接働きかけることはできません。 ですがそれでも、関わり方を工夫することはできるかも知れません。

以前、ソフトウェア開発を委託する立場だった時期がありました。 そのとき心がけていたこととして、報告される失敗に対して深刻な印象を与えないようにする、というものがあります。

深刻になり失敗してしまったことに心がとらわれていると、課題の解決のための力が削がれてしまいます。 当時はそこまで意識していたわけではないのですが、解決のために深刻にならないように、しかし真剣に真摯に取り組めるように、感情の余計な負担を減らしたかったのだと思います。

失敗したらどうするか?

共有しましょう。

なぜそうなっているのか。 その背景を知らないでいると、不用意に「改善」してしまい、失敗によって得られていた知見を失うという誤りをおかす危険があります。 その結果、その知見によって防がれていた課題が再び現れ、重大な事故につながることもあります。

失敗することが、うまくいかないことを知るための行為であるなら、その知識を活かさない手はありません。

ここでもまた「失敗を共有したらあげつらわれる」ことがあると、心理的な負担や障壁になってしまいます。 そうならないためにも、心理的に失敗できる環境が重要なのだと思います。

失敗のために

高い頂には広い裾野が必要です。 そして失敗は、成功のための裾野だと思います。

失敗だけが裾野ではありませんが、失敗もまた裾野を広げる役に立っているように思います。

過ちをおかすのではなく、裾野を広げるために、積極的に失敗することができたらと思います。