タオルケット体操

サツバツいんたーねっとでゲームとかガジェットのレビューとかをします

どうしてスタートアップのソフトウェア設計はいつもいつもブッ壊れてるんですかぁ?

そんな皆さまの疑問にお答えします。

スタートアップのアーキテクチャがブッ壊れてるのってなんでェ?

先にざっくりまとめましょう。

巷でよく言及されるのはカネ、つまりは雇用するエンジニアの能力問題を元凶とする方が多いようです。
スタートアップの内情を知っていれば金と雇用の問題がどれだけ切実であるかについて異論を唱える人はいないとおもいます。しかし僕の考えによればこれはスタートアップのソフトウェア開発が抱える問題のうちのひとつ側面にすぎません。

つまりどんなに優秀な人間をかき集めようがスタートアップのソフトウェア設計は近い将来壊れる宿命にあるということです。

スタートアップの存在意義は未来の不確実性そのもの

続きを読む

コードの修正範囲は最小限に留めておくべきなのか?

一つの機能修正、バグ修正に対してコードをいじる範囲、つまりdiffの行数は常に最小であるべきなのか?というお題ですね。

無関係な場所を触りまくって肥大化したPull requestが論外なのは当然です。
そのせいでその挙動に影響があった範囲だけど少しだけいじるようなPull requestが常に理想、と素朴に信じている人が多いように見受けられます。

しかしいじるコードが小さければ小さいほど良いという風潮には僕は反対です(デカければいいって主張はしてないからね)。

コーディングはプロジェクトに対するデザイン行為である

僕はコーディングそのものをデザインだと考えています。
木をみて森をみず、といいますがコードもそのときどきで視界にはいっているロジックや関数単体のみで考えてはいけません。
プロジェクトに含まれるコードは視野にある文字列よりもその外側にあるもののほうが大きいのです。だから常に脳内にあるそれと対話し、追加や修正や全体との調和を考えながらやらなければなりません。

いまの話はあまりに本質的すぎてうまく例にできるようなサンプルが見当たらないのですが、例えば

「ある関数Xに渡している引数aの形式が間違っていた」

というバグがあったとします。
これを修正するのは簡単です。引数aの形式を間違えている場所を開いて正しい形式にして渡せばいい。
ただ本質的な問題は、そもそも形式を間違えたら動作しないような関数になっているのが悪いわけです。こういうのはたいてい引数の型の宣言が弱かったり、引数aの想定してる形状が複雑すぎるのが原因です。
適切な型宣言を使うようにしたり、複数の引数に分解するよう誘導すれば良いのです。

ただしこの修正を取り入れると、その関数Xを使っている箇所全てにdiffが発生します。
本質的に正しい修正のはずでも、いじるコードを最小限にという原則には外れてしまいますね。
もちろん一度挙動の修正のみを行うPull requestを作り、その後リファクタリングタスクとして別のPull requestにするという方法もとれなくはないですが、手続きを煩雑にしているだけでやっていることは同じです。

言語仕様の高級さとコーディングスタイルの関連性

ここまで書いて気がついたことがあります。
僕の主張は最低でも静的な型検査がついている、あるいは結合テストの網羅性が高い状態にある(理想状態ってかんじ)ことを前提にしていました。

例えば僕が普段愛用しているTypeScriptは静的型検査付きとしてはユルいところの多い言語ですが、それでも strict: false のような超人向き苦難厄憑きモードオプションやanyをそこらじゅうで使う野獣派スタイルコーディングを採用でもしていない限り、各インターフェースの変更は依存箇所全てに通知されます。

そうではない状態、つまりどこか一部分のインターフェースをいじると全然関係ない場所でなんかのロジックがぶっ壊れるようなコードを生み出しやすい言語、フレームワーク……*1で書き続けている場合、予測不可能性に対する防衛術として「コードの修正範囲は最小限にしろ!!」という鉄の掟が生まれるのかもしれないなと予想しました。
まぁ実際グローバルに書きかわっちゃう変数とか、特定のオブジェクトのプロパティを参照してメソッドが生成されまくるようなメタプログラミングとかそういうことをしてると予想外の壊れ方しますから、そういう環境であればいくらテストの網羅性を上げていたとしても予測不能な壊れ方をしてしまいがちなわけで。であれば必要なところ以外いじるんじゃねえというルールは理に適ったものになるわけです。
現実は厳しい。

あるいはGitやマージツール使いこなしの練度

"マージツール"と書きましたが、筆者はめんどくさがりなのでいつも端末上で筋肉式のコンフリクト解消してます。だってめんどいし……困ってないし……

それはともかく、Gitの練度が低い方はコンフリクトを異常に怖がる、避けたがる傾向にある気がします。
実際、度を超えた巨大なPRをマージしたときに生じたロジック的なコンフリクトの根本的解消は目視によるところが大きく、労力も難易度も危険度もマジでハンパねえので避けるべきですが、常識的な範囲のコンフリクトでさえも恐れてそれを避けようとする場合は先の「高級問題」とこのGit練度が影響しているのかもしれない。しれませんね?たぶんね。

結論

プロジェクトの状態、チームの方針に従え。

なんともひよった結論やね。


ちなみに今回のエントリの内容はメンバー間で合意をとれるプロジェクトを前提にしています。
自信がメンテナではないOSSにPRを送る場合、目的に対してdiffは最小限であるほうがたぶんいいんじゃないかなとおもいます。

*1:こう書くとなんだか馬鹿みたいだけど実際には存在しがち

PROのためのスーパー型が付くReactフォームライブラリをつくった

GitHub - hachibeeDI/mayoiga

hachibeedi.github.io

つまりReactフォーム界のHANAMASAということ。

良い特徴、あるいはReact Hook Formとの違い

1. refに依存していない

自作するとわかるのですが、Formの状態管理(およびバリデーション)は素朴な実装だとあっという間にUIをブロックするレベルでパフォーマンスが劣化します。
Reactが難しい、と言われる一つの原因ですね。

React Hook Formは各inputをUncontrolled Formとする代わりに、状況に応じてDOMを直接操作することで、普通のフォームであれば適当に実装してもパフォーマンス劣化が起こりにくいです。
昔はrefを直接渡していたようにおもえますが、いまは <input {...register('hoge')} /> というようにインターフェースを隠蔽することが推奨されています。もしかしたら将来的にrefを排除することも考えている可能性はあります*1が、少なくとも直近のバージョンではそうでした。

APIがシンプルでパフォーマンスも素晴らしい、いいことづくめじゃん!と思われるかもしれませんが、当然デメリットがあります。
というかちょっと使い込んだらデメリットがモリモリと明らかになったのが今回フォームライブラリを自作した理由の一つです。

例えば……refに依存している以上、中身の挙動がDOM操作なので初期値の導入まわりでReactのlifecycleと競合を起こします。
またuncontrolledな中身をref越しにどうやって操作しているのかはブラックボックスです。なので useImperativeHandle を使っていると意味不明なエラーが出まくります。
またこの シンプルなAPI は簡単なフォームの作成に特化しているので、refを公開しないような自作あるいはサードパーティの複雑なformを使い始めた瞬間にコード量が増え、APIは非直観的なものになります。

これらを解決するために作ったのがMayoigaです。
ゆえに「 useImperativeHandle なんてつかわねーよ!」というプロジェクトであれば、あるいはrefへの依存に居心地の悪さを感じないのであればHook Formで十分かもしれません。

2. バリデーション前と後で違う型をつけられる、その他の場所でも型がめっちゃつく

はい。

*1:refなしであのシンプルなAPIを保てるとはおもえないですけども

続きを読む

異説 ぼくがかんがえた最強のフレームワーク

注意:本稿にはあまり一般的ではない(かもしれない)筆者の独自思想がふんだんに盛り込まれています。これを受けてどう考え、行動するのかは自己責任でよろしくおねがいします。

ソフトウェア開発に銀の弾丸なし、という言葉は広く市民権を得ています。多少なりとも開発の経験がある方ならばこれに異を唱える人はいないでしょう。

しかしそんな我々もアーキテクチャ(多くの現場でこれはフレームワークの選定と同義語である)の話になると無意識のうちに「最強の何か」を想定して思考してしまいがちです。そうだよね?

なので未来の自分へのメッセージもかねて、いまここではっきりと宣言しましょう。   この世界に最強のアーキテクチャは存在しません。各プロダクトに合わせた最適が存在するだけです。

続きを読む

型はみんなの心の中に存在しているんじゃない?

はじめに

まずインターネットのお気持ち記事で「型がある」とか「型がない」とか言い始めたら、書き手がどういった意図でそのような言葉を選んだのか警戒しなければならない。

全部を語るとそれだけで5記事分くらいの分量になりそう*1なので割愛するが、世間的には「静的な型検査を行える」ことを指して「型がある」と呼称しており、その強弱とかランタイムの挙動については基本的に無視するという姿勢をとっているようだ。きちんと研究している層にとってはなんとも歯痒い定義なのかもしれないけれど、雑に語るにはとても便利な切り分けなので本記事も基本的にこれを採用する。

余談だけど、世間一般が語る「強|弱い型付け」の定義って恣意的すぎて意味不明だわとおもってたんだけど、いまWikiで確認してもやっぱりどういう基準で決めてるのか意味不明。たぶん時代遅れの概念なんだとおもう。

  • はじめに
  • 変数の型が静的なのは我々にとってごく自然なこと
  • 関数の存在は人類には早いので型は静的なほうが優しい
    • 補足:ジェネリクスを知らないという枷
  • 型検査は動作チェックの自動化
  • じゃあなんすか、型なし言語にはなんの優位性もないっていうんですか
    • 1. メタプログラミングがやりやすい
    • 2. プラグイン的なものへの活用
    • 3. その他
  • まとめ

変数の型が静的なのは我々にとってごく自然なこと

*1:ついでに筆者は十分な知見をもっていない

続きを読む