パルカワ2

最近はFlutterをやっています

画面のWidgetテストはなにをテストするべきか

最近はよくWidgetテストを書いている。Presentational Widget と Container Widgetに分けていて、Presentational Widgetは、Riverpodなどに依存せず、見た目のことにだけ集中出来て大変よい。テストも見た目のテストのことだけに集中できる。

zenn.dev

Container WidgetがPresentational Widgetを使うという関係性なのだが、Container WidgetのテストではPresentational Widgetでテストしたことはテストしない。小さくテスト出来るのであれば、Presentational Widget側でテストするべきだと思う。

一方で、Widgetは他のWidgetの影響を受けやすいように感じる。例えば、状態AのときにBとCの項目が表示されていて、状態Dのときは表示されていないとか。1つのWidgetで完結していればいいが、離れている場所にあるとかだと難しい。そのときはうまく連携しているのかのテストを書く。Presentational Widgetに変更が入るとContainer Widgetのテストもコケて正直めんどいが、変更が入ったことに気づけてどのように変化するのか見れるのは良いことだと思う。

加えて、妙に長いテキストが入ったときとか文字サイズが変わったときとか、Widget同士で干渉してRenderFlex Overflowが起きないかというのは見たいように思える。Presentational Widgetごとに確認しても、結局他Widgetとの関係が影響するように思うので、広めで見てもいいように思える。

このあたりはGolden testである必要はなく、ランダムの値を使ってProperty based testingのようにテストするというのも出来て面白いかもしれない。

衝動を育む場

孤独に生きよを読んで、岡本太郎の自分の中に孤独を抱けを思い出したので読み返していた。別に孤独に悩んでいるわけではないです。

全体的に"挑め!!!戦え!!!考えよ!!!"という話だと自分は理解したのだが、衝動の話が面白かった。太郎いわく衝動という内側から湧き上がる根源的なエネルギーは誰しもが持っていて、生きがいをもたらす原動力らしい。

 「創造すること」は人間の本能的な衝動だ。

岡本 太郎. 自分の中に孤独を抱け (p. 121). (Function). Kindle Edition. 
 〝描きたい〟というのはつまり、なにか外のものではなく、内にあるものを溢れ出させたい、表現したいという衝動のはずだ。

岡本 太郎. 自分の中に孤独を抱け (p. 122). (Function). Kindle Edition. 

仕事においてもこういう表現したい衝動はある気がしていて、自己形成やキャリア形成で話される「こういう人間になりたい」「こういうことをテーマとしてやりたい」は言い換えると「こういう表現が出来るようになりたい」といえる気がする。

例えば自分は数年前は「変化につよいエンジニアになりたい」がソフトウェアエンジニアとしてのテーマだったわけですが、それに対する衝動はあってそうなるために色々挑戦してきたし、それが支援されていたように思う。今は別のテーマを持っていて活動しているのだが、やることが変わるのでテーマも変わるかもしれない。

この衝動が減退している状態が、孤独に生きよなどで語られる思考停止状態なのではないかと思った。

みんなで衝動を育む

衝動が減退することあるよなぁ!と思っていて調べたら、問いかけの作法に「衝動の枯渇」という定義があった。衝動が枯渇しているときは、思考停止するという話。読んだときにも言及していた。

このように「逸脱の抑止」は、チームメンバーの内発的な動機を阻害する「衝動の枯渇」という現代病を生み出します。衝動とは、人の内側から湧き上がる欲求のことで、子どもの頃から誰しもが持っている本能的な感覚です。ところが、規範から逸脱することを恐れ、関係性が凝り固まったままでは、それが主体的な行動や発想のストッパーとして働き、本来あるはずの衝動に「蓋」がされた状態になります。

安斎勇樹. 問いかけの作法 チームの魅力と才能を引き出す技術【DL特典付き(未収録原稿)】 (p. 54). (Function). Kindle Edition. 

枯渇まではいかなくても減退することは「逸脱の抑止」以外にもあるように思える。

  • 取り掛かりたいテーマが巨大すぎて一人ではどうにかするには大変すぎるが時間もないし周りからの支援がない
  • 取り掛かりたいテーマを見つけて、直すように周りに提言をしたが、周りが「言い出しっぺだからやってよ」と言ってきて、全部やることになるのが続いている
  • 諦める理由が大量にあり、諦めることが常態化している
  • そもそも取り掛かりたいテーマがわからない

これ以外にも子どもが生まれたとか介護が始まったとかの変化によって集中したいものが変わることもあるとは思うし、どうにか出来ることとどうにも出来ないことはあるだろうが組織による支援によってみんなで衝動は育むことも減退させることも出来るんじゃないかと思える。

ここまで書いて気付いたが、衝動を育むにはどうすればいいかは、内発的動機づけでめちゃくちゃ語られているような気がするし、衝動と内発的動機は一緒じゃんってかなり今更気づいた。

人はなぜ働くかでいえば「お金」と「自己形成」のためでしかないと自分は思っていて、自分は比較的お金の優先順位は低いほうなので、自分にとっては職場がみんなで衝動を育む場であることがかなり重要だなと思った。

孤独に生きよを読んだ

「奴隷になるな」というのがこの本のパンチラインだと自分は思っていて、ここでの"奴隷"とは外圧によって思考停止に立たされた人を指している。この本に書かれているのは、その奴隷にならないために、思考し続けなさいという話であると自分は理解した。

人間同士だからそういう食い違いは日常茶飯事のはずですが、では実際に疑問を相手に伝えているかといえば、多くの場合は伝えていない。実害があるわけではないし、それはそれでそこそこやっていけているから、なんとなく黙したままで済ませてしまっている。  こうした状態も思考停止ですから、奴隷だと言えます。

田中慎弥. 孤独に生きよ 逃げるが勝ちの思考 増補改訂版・孤独論 (p. 15). (Function). Kindle Edition.

孤独に生きよでは、内容を理解できなくてもいいから本を読むことが思考することになり奴隷から脱出できる方法であると書かれている。

本を読まなくても生きてはいけます。本を読まないからといって飢えるわけでも、病気になるわけでもない。  それでもわたしは、読書を強く勧めます。  いまあなたを取り巻く環境とまったく異なる世界が、この世のどこかに間違いなく存在するという実感を書物はもたらしてくれます。読書を通してあなたはいろいろな人生を体験することができる。

田中慎弥. 孤独に生きよ 逃げるが勝ちの思考 増補改訂版・孤独論 (p. 73). (Function). Kindle Edition.

これは、結構人それぞれ方法があるんじゃないかと思っていて、自分の場合は読書もそうなんだけど、最近だと山に登っている間に「山登りはプロダクト開発と同じだ…」と類推したり、早朝の新幹線の中で「怠惰であれ!」と書いたりする時間も本を読むことと同じように思考する時間だと思える。

少なくとも自分は、20代と比べると本を読む時間がかなり減っている。これは、環境による衝動の減少や思考停止して見れるエンタメに触れる機会が多くなったからだと思うのだが、あえてそこから離れて孤独になる時間、思考する時間を取るようにするというのは、生きていくうえで非常に重要なのだろうと思う。

一方で、独りで思考するだけではなく、他の人と考えたことを共有することで自分だけでは発見出来なかったことが発見出来るとも思っていて、1 on 1とかでこういう本を読んでこういうことを考えてるんですよねから色々話がつながって自分では発見出来なかったことが見つかったりするので、孤独時々対話くらいがちょうどいいのかもしれないと思った。

返済しやすい技術的負債

技術的負債について自分が考えたことをメモする。

意図的な技術的負債と不注意な技術的負債

  • 技術的負債を負うとき、意図的に負うか不注意で負うかがある。いわゆる、技術的負債の4象限
  • 「期日があるから負債を負うが、改めてあとで直しましょう」で生まれた負債と「何も考えずに実装しました」で生まれた負債は全く異なる

開発中、技術的負債を意図的に負うかどうかの判断力は人やチームによって異なる

  • 意図的に負債を負うか判断する力を持っているかは、人やチームによって異なりそう
  • 意図的に負債を負うか判断する力は、経験と内省で得る再現性が高い能力な気がする。少し前に技術力とはなにかで話した訂正しやすくつくる力の一部だと自分は思う
  • とはいえ、能力の問題だけではなく期日に追われていて正しい判断ができる状態になかったなど組織的な問題もあるはず。個人だけのせいではない。

意図的な技術的負債と不注意な技術的負債の違いは、返済しやすく出来るか

  • 意図的な技術的負債は、返済することを前提に出来るのでコードやGitHubのプルリクエストにコメントを残したり、テストを書いておくなど後々返済しやすいようにするための活動ができる
  • 不注意な技術的負債は、そもそも負債だと気づいていないのでそういう活動は一切ない。なので、コードを読んで直すしかない。どんなにコードを読み書き出来ようが所詮人間なので読み間違えたりするとバグが起きる。なので、かなり気をつけて作り直したりする必要があってコストが大きくかかり返済がしにくい

返済しにくい意図的な技術的負債もある

  • 意図的に技術的負債を積んだからといって、自動的に返済しやすくなるわけではない
  • 返済しやすいように活動する必要はあり、それが出来るかは人やチームによって異なる
  • これもやれるかどうかは、経験と内省で得る再現性が高い能力な気がする。これも訂正しやすくつくる力の一部だと自分は思う

技術的負債を作る人間と返す人間が別だと良いことがない

  • 他人の借金をずっと返していると「なぜ私が返しているのか?」となる。技術的負債も同じで、技術的負債を作る人と返す人が別だと技術的負債を返す人は「なぜ私が…」となる気はする。
  • 「仕事だから」で片付けられる人も片付けられない人もいる。人間だもの。
  • また、技術的な負債を作る人も返すことがないので、経験と内省ができず、訂正しやすくつくる力を身に付けられない

まとめ

  • 意図的に負債を負うか判断していくぞ
  • 意図的に負債を負うときは返済しやすいようにコメントやテストなりを書いていくぞ
  • 負債を作ったら返すぞ

怠惰であれ!

仕事において、泥臭くやる必要があるときは多々ある。ここで言う「泥臭くやる」とは、短い期間でそれなりの成果を出すために、自動化や仕組み化などを後回しにして、まずは手動で地道に作業を進めることを指す。これは、とにかくすべてを捨ててでもスピードが求められる初期段階などでは有効な手段となる。

一方で、泥臭くやり続けることは避けなければならない。例えば、サービスを早くリリースすることを優先して手動でのリリース作業から始めたものの、それが常態化し、いつまでも手動リリースを続けてしまうようなケースは、「泥臭くやり続けている」典型的な例と言える。

ここで思い出したのがプログラマーの三大美徳と言われる「怠惰*1・短気・傲慢」である。もし、泥臭くやり続けることを「勤勉」と捉えるならば、その対極にあるのは、三大美徳の一つである「怠惰」と言えるかもしれない。ここでの怠惰とは何も考えずにやるべきことをやらないというわけではない。未来の自分を含む他者が楽をするためにより効率的な方法を模索し、自動化や仕組み化を進める原動力となるのがこの「怠惰」の精神である。

また、10Xには「運営自律性」という考え方がある。この特性は、パートナーが何かを実現したいと考えた際に、10XのBizDevやソフトウェアエンジニアなどの手を介さずに、パートナー自身が自律的にそれを達成できるかどうかを示す。例えば、パートナーが何か新しい設定を試したいと思った時に、その都度10Xのメンバーが設定ファイルを書き換えたり、ソフトウェアをデプロイしたりする必要がある状態は、運営自律性が低い。運営自律性を高めるためには、自動化や仕組み化などが必要になっていく。逆にいえば、10Xメンバーが怠惰の精神を持っていないのであれば、運営自律性はずっと低いままだろう。

speakerdeck.com

これらのことを踏まえると、日々の業務において発生する泥臭い作業は今あえてやっているのか、勤勉さゆえにやっているのか考えて働くことが非常に重要だと思える。そしてそのためには怠惰であることが重要なのだろうと思う。

*1:怠惰としているが、怠慢、無精など色んな訳があった。自分的に語感がいいのを選んだ

対応できると運用できるは別

「対応できると運用できるは別」というようなことを同僚が言っていてめちゃくちゃよかった。自分なりの解釈を書いておく。

開発をしていると「このタスクの変更箇所のコードはめちゃくちゃ。無理をして対応出来るが、リファクタリングや作り直しなどの時間をとって実装しないと運用ができない」と感じることがある。

対応できるとは

対応できるとは、無理があろうが言われたものをそのまま作ることである。保守性のことはないがしろにしてでも何がなんでも実装する。仕事であれば「対応すること」だけを考えることもあるだろうが、それだけしか考えていないと後々運用するときコストが大きくなる。

運用できるとは

自分が思うに「運用ができている=問題が起きてない状態」ではない。無理矢理対応したが、リリース前のテストを頑張って色々とバグは潰せたし、リリースしたあとに問題が起きてないからうまく運用が出来ているね、とはならない。

運用ができるとは

  • 安く早くチームでシステムに変更が加えることができる
  • 安く早くチームで異常に気づくことができる
  • 安く早くチームで異常を収束させることができる
  • 安く早くチームでシステムをリリースすることができる
  • 安く早くシステムが動く
  • 異常時の影響範囲が小さい

ぱっと思いつくだけでこれだけあったが、これら以外にもあると思う。これらが出来ていることで「運用ができている」といえると思う。

「運用できるか?」という問い

開発のなかで「運用できるか?」という問いに答えを出せるのは実際に開発をするソフトウェアエンジニアにしか出来ない。ソフトウェアエンジニアが、この問いを持たなければ、「運用できる」状態から遠のいていく。なのでソフトウェアエンジニアが「この開発をして運用できるか?」と問い答え続けるのは持続可能なソフトウェア開発において非常に重要だと感じる。

さらにいえば、運用できている状態の解像度は人によって異なる。自分もここで「運用できるとは」を書いたが、もっと詳しい人から見ればまだ考えることがあるのだろうと思う。その解像度を共通認識にしてくことというのも重要であると思う。

UIで使うデータクラスでIterableを使うかListを使うか

UIで使うデータクラスのメンバー変数にIterableでデータを持たせていた。Listが持つaddやらなんやらをしてほしくないからそもそもそういうIFを提供しないのが良いと思っていた。

@freezed
class User with _$User {
  User({
    required Iterable<Favorite> favorites,
  }) = _User;
}

一方で、DartのIterableは遅延処理される。

例えば以下のようにmapの中で例外を投げる実装があったとする。これは遅延処理されるので、buildメソッド内で例外が発生しうる。

@freezed
class User with _$User {
  User({
    required Iterable<Favorite> favorites,
  }) = _User;
}

final provider = FutureProvider((ref) async {
  final user = await ...; // なんらかの方法でUserResponse取得
  return User(favorites: user.favorites.map((e) {
    swtich (e) {
       case ...:
         return Favorite();
       case ...:
         throw UnknownException(); // 例外を投げている
    }
  }));
});

class UserView extends ConsumerWidget {
  @override
  Widget build(context, ref) {
    final data = ref.watch(provider); // AsyncValue<User>を返す。ここではまだmapの内容は評価されていない
    return data.map(data: (data) {
      print(user); // ここではまだfavoritesは評価されていない
      return FavoritesView(data.favorites); // ここでfavoritesが評価されるので、ここで例外が投げられる可能性がある
    }, loading: (_) {}, error: (_) {});
  }
}

どこで評価されるのか/すでに評価済みかもわからないので、AsyncErrorにいくと思って書いていても、buildメソッド内で例外が起きてしまう。そしてbuildメソッド内で例外が起きるとErrorWidget.builderを定義していない限りWidgetが表示されないのでユーザーはなにが起きてるのかわからない。

Hixieさんは、buildメソッド内で例外を投げないことを推奨すると言ってる。それを防ぐ仕組みがないのにそんな無茶な…と凡人の自分は思うのだが、まあ言ってるのでしょうがない。

github.com

Widgetで利用するデータは、すべて評価済みのものを渡すのがよさそう。そう考えるとUIで使うデータクラスは、基本的にListに依存するのがよく、必要であればIterableとするのがよいのかもしれない。