旧gaaamiiのブログ

間違ったことを書いている時があります。コメントやTwitter、ブコメなどでご指摘ください

リモートワークについて

最近仕事どうやってやるのがいいかなあと考える機会が多く、その中でリモートワークについても考えることがある。今の所は以下のように考えてる。

自分はフルリモートじゃないと絶対嫌だという気持ちはないのだけど、考えてみれば往復2時間くらい毎日使うのはもったいないよなあと思う。たとえば週3日のリモートワークが許されているとして、時給が2000円だとしたら、往復2時間×週3日×2000円×4週間×12ヶ月って考えて、576000円!?お得!ってなる。実際、自社にリモートワーク制度ができたときは少し感動した。

もちろんリモートワークはチーム的には工夫しないといけないこと(ビデオ会議どうしようとかちょっと相談したいときどうしようとか)が出てくるので最初は面倒だとも思うし、一緒に働く人とオフラインでワイワイする時間もあったほうがいいと思うんだけど、通勤時間は当たり前に受け入れすぎて忘れがちだけどなかなかの損だよなあ、何も週5日でやらなくても、という思いもある。リモートワークがどうしてもできないとかではなくちょっとした工夫で成り立つレベルであれば取り入れたほうがよさそうだと考えている。

Custom TypesのデータをJS界に受け渡しする(Elm)

Elmでアプリケーションの状態をlocalStorageに保存しとくようなものを作っていて、カスタムタイプはどうするのか知らなかったのでメモ。

雑にそのままportに渡そうとしたところ、以下のコンパイルエラーが出てきました。

The setStorage port is trying to transmit a ColorTheme value:

259| port setStorage : ModelExposedToStorage -> Cmd msg

I cannot handle that. The types that CAN flow in and out of Elm include:

Ints, Floats, Bools, Strings, Maybes, Lists, Arrays, tuples, records, and JSON values.

Since JSON values can flow through, you can use JSON encoders and decoders to allow other types through as well. More advanced users often just do everything with encoders and decoders for more control and better errors.

上の ColorThemeというのが、localStorageに保存したかったけどできなかったカスタムタイプのデータですね。具体的にはこういうものです。

type ColorTheme = WhiteTheme | DarkTheme

で、エラーに出ている通り、

Since JSON values can flow through, you can use JSON encoders and decoders to allow other types through as well. More advanced users often just do everything with encoders and decoders for more control and better errors.

JSON encodersとdecodersとやらを使えばいいようです。

対応

こんな感じの変更を入れました

どういうこと?

localStorageに保存するときにJSONの形にencodeして、localStorageから取り出してきたときにはElmのデータにdecodeしてます。問題になったColorThemeのところは、こんな感じでcaseで文字列にしたやつをencodeしているだけです。

素朴に趣味開発をやっていく

趣味開発は楽しい。楽しいけど時間とモチベ保つのが大変。そのため、以下のようなポリシーで素朴に趣味開発をやっていきたい、というメモ。

  • 自分で使うものは自分でつくる
  • すぐに完成させるつもりで始めない
  • 何度でも書き直す

自分で使うものは自分でつくる

自分で使うものは自分でつくるという感じでやっていきたい。ブログ下書きに使うMarkdownエディタとか、TODOリストとか、RSSリーダーとか。それで稼ごうとか流行らせようとかは一切考えない。

幸いMarkdownエディタもTODOリストもRSSリーダーも、既存のもので自分にとって完全にしっくりくるものがないので、趣味開発のネタに困ることはなさそう。

すぐに完成させるつもりで始めない

自分で使うものといっても比較対象は一流の他のアプリになってしまうので、最初からそこを目指してしまってつらくてモチベーション保てないということになってしまう。本当に自分がそれが最高といえる状態が完成だとして、そこにたどりつくのにどれくらいかかるかはわからない。だから、完成させるつもりで一気に頑張ろうとせず、完成するまではその不便さと付き合う覚悟を決めてそれに接する。不便だと直したくなるので、モチベーションは保たれる。

何度でも書き直す

せっかく仕事と違って自由なので、リニューアルしたいときにできる。技術的にこれ使いたいからという理由で書き直してしまってもいい。

で、なにやってるのか

最近は https://nekobito.github.io をちまちまつくってる。

JSON色付けたいへん問題

この前、以下のツイートを目にして、面白いな〜と思いました。たしかにフロントエンドの仕事は、サーバーからJSONとして返ってきたデータを人向けに表示するという仕事が多く、雑に言い表せてる感じが面白い。

上のはジョークだと思うんですが、とはいえ真面目に考えてみると、JSONを人に見やすくする作業をひたすらやっているのだから、もう少し仕事楽になってもいいんじゃないか?なんでJSON色付け係は相変わらず大変なんだ?という疑問が湧いてきます。それについて、自分なりに考えを整理しておきたいと思いました。

JSON色付け係は何をしているのか

自分の最近の仕事を振り返ると、JSON色付けの仕事は前準備と実装の2段階に分けることができます。

前準備

機能を作る以前に、1行のコードも存在しない段階。

言語やライブラリの選定

もちろんやることによって何を選ぶべきか違うと思うのですが、やることが一般的なJSON色付けなら選ぶべきものは以下の4つになると思います。

言語

ブラウザならJavaScriptで書けばいいんだろうという感じですが、ES5で書くのがしんどいけど、ES5で配らないとちゃんと動かない環境があり、そういう環境でも使ってもらいたいので、なにかしらの工夫をしてES5のJavaScriptに吐き出すような仕組みを使ってます。ここでやるのは言語選びみたいなものです。ES6で書くか、TypeScriptで書くか、はたまたほかのAltJSで書くか。

画面表示

画面に表示するものを、こういう感じで書きたいです。

<h1>{{ blogPost.title }}</h1>

こういったテンプレート機能みたいなもので、このデータはここに表示されるというのを書けるようになります。

また、パフォーマンスでいうと、画面の差分更新(仮想DOM)も多くのライブラリがサポートしている機能です。

ja.reactjs.org

状態管理

ユーザーが画面操作すると状態が変わって、画面も変わる。というのがだいたいのアプリケーションに必要な挙動です。 これをするには、状態をどっかで持っておく必要があります。Reduxとかだと一箇所に状態を集めて、react-reduxで必要なコンテナとつないで状態入れるみたいなやりかたをします。

ルーター

ページ遷移をDOMの書き換えで表現するやり方(SPA)が流行りです。なんで流行ってるかというと、そうするとサーバーとやり取りするデータ量やページの再描画する部分が減って動作が速くなったりするからですが、そういったことを実現するにはブラウザ側に、このURLだったらこのページを表示するみたいなルーターが必要です。

ビルド設定

あと、上の言語選びでも書きましたが、最近のフロントエンドはソースコード書いてサーバーにアップロードして終わりではなく、モジュールを結合して圧縮されたES5に吐き出したりする必要があります。そこで、webpackなり何なりをつかってどうやって吐き出すか、という設定を書いたりします。

実装

一通り整ったら、機能追加の際にコードを書き足すだけになっているはずです。画面を書いて、振る舞いを実装して、色を付けたりします。ここで、なるべくデザインの意図や使われ方を理解して再利用しやすい形にしておけば、以降の実装の手間が減ります。

そこで、コンポーネント指向だったりAtomic Designだったりなんやかんやの方法論で、モジュールや画面パーツの再利用性を実現します。

なにが大変なのか

だいたい上に書いたようなことをやっているわけですが、それを踏まえて改めてなにが大変なのかと考えると、やっぱり前準備がけっこうたいへんです。○○アプリを3ヶ月でつくりたい!といったときに、前準備に1ヶ月かけてしまっては相当つらいですが、何も考えてないゼロの状態から始めると1ヶ月くらいかかってしまいます。

また、ディレクトリ構造やモジュール分けをそのJSON色付け係のセンスで行ってしまうと、実装の属人性が高い状態になってしまいます。

どうするべきか

ライブラリをあれこれ選ぶことはできればプロダクト開発以前に終わらせていて、開発が始まるときには、Railsみたいにコマンドばしばし叩くと雛形が出来上がるような仕組みまでできているとよさそうです。

たとえばangular-cli だと、だいたいアプリケーションに必要なものがng generateコマンドで作れそうです。

ただ、じゃあみんなAngular CLI使えばいいかというとたぶんそんなことはないので、どのライブラリやフレームワークが適しているかなども含めて考えてこれくらいのセットアップやらその後の保守をできるJSON色付けの専門家が、コマンド叩けば適切な場所へのモジュール追加やビルドができる環境を提供する、という形がいいのじゃないかなと考えています。

で、実装者の方はドメイン知識を活かした名前付けだったり、ユーザーの気持ちよさを実現するための技巧を凝らしたCSSアニメーションだったりに集中することができると、整った開発環境の上で、安心してイケてるウェブアプリケーションを作っていけるのではないでしょうか。

それなりに大変

と、ここまで書くと、フロントエンド環境の提供みたいな部分は、JSON色付けとは全く別物ですね...。どちらかというと、実装者のほうがJSON色付け係と言えそうです。

実装するのがJSON色付け係であれば、フロントエンド環境を整える人にもなにか係名がほしいなと思いますが、実際の現場ではそもそもフロントエンド専任者というポジションでさえとても少なく、フロントエンドなんて呼び名でも贅沢なのにその中でさらに名前を持つなんてことは湯婆婆が許すとは思えませんし、その担当が分かれていることは実際にはほとんどないと思います。

ただそれはあくまでも湯婆婆の都合なので、JSON色付け係と呼ばれる仕事の中にも実はいろいろ属性の違うものが隠れとるんだということを忘れなければ、いちいち「フロントエンドなんて大したことやってない。おれはデザインできないしサーバーサイドは中途半端だしデータ分析なんて一切できない...うわあああ」みたいな気持ちにならなくて済みそうです。もちろん全部できる最強な人も世の中にはいるのですが、まあそれはおいといて、JSON色付け係もそれなりに大変だし、一つ一つ丁寧にやってくしかないという気持ちを改めて持ったのでした。

ISUCON9(予選)の振り返り

今年も、 id:karur4n と二人で、くもキャストチームとしてISUCON9に参戦しました。楽しかったですが、今年も予選突破できず悔しい。

スコアの発表を見ると、

2,600  くもキャスト

でした。

isucon.net

例年、我々のような弱小チームに語れることなどない...という気持ちであまり真面目に振り返っていなかったのですが、我々のような弱小でも弱小なりに楽しめるんだぞというのが(まだ出たことない人などに)伝わればいいかなと思い、振り返りを書いてみました。

※ISUCONってなに?という方はこちらをご覧ください。

qiita.com

お題

毎年楽しみなお題ですが、今年はIsucariでした。椅子を販売できるサイトらしい。

f:id:shgam:20190908003340p:plain

開始時間の変更

ポータルサイトに不具合があったようで、10::00 -> 10:10に変更されました。我々は、Discordで通話してたんですがマイクの入力がイヤホンからになっていたのでMacの内蔵マイクにしたりとかそういうことをしていました。

Alibaba Cloudコンソールでイメージ展開

迅速に問題は解消されて、予告通り10:10に開始しました。

今回は、予選マニュアルの通りにAlibaba Cloudのコンソールでサーバーにイメージを展開するところからのスタートでした。前回までのISUCONは特にサーバーのセットアップが必要なく、ポータルサイトに入るとチームに与えられたサーバーのIPが表示されているので、そこにsshで入って作業を開始するという流れでした。

設定・起動を終え、サーバー名とGlobal IPとPrivate IPをポータルサイトに登録、Alibaba Cloudのコンソール画面でログインのために作成/設定しておいた鍵を共有、ローカルの/etc/hostsを編集して起動したIsucariの画面を確認するなどしました(10:30)。

f:id:shgam:20190908002040p:plain

ソースコードをプライベートリポジトリへ

id:karur4n に、Githubにプライベートリポジトリを作成、ソースコードを置いて、Contributorに追加してもらいました(10:48)。

使う参考実装はNode(TS)とSinatra(Ruby)どっちがいいかね〜みたいな感じだったので、その判断するために実装読んでもらっていたりもしました。TSでいきましょうとなって、TSでいくことに。

最初のボトルネック探し

また、alpを使ってボトルネックを探すのも、 id:karur4n がやってくれていました(11:18)。

f:id:shgam:20190907222211p:plain

2台構成にする

マニュアルを読むと、3台まで使って良いことがわかったので、ウェブ用とDB用に分けようということで、同じ手順でもう1台サーバーをたてました。この新しいサーバーのプライベートIPを環境変数MYSQL_HOSTにすればええやろと思ったのですが、ベンチを走らせたところ、どうも接続できていない(11:23)。

セキュリティグループを疑って、3306ポートを開放するルールを追加(11:41)。

しかしうまくいかない...。ここでVPCの仕組み自体の理解が浅いために、あれ、プライベートIPの指定じゃだめ?なんか追加で設定必要?などなどの寄り道をします。寄り道をしたあげく、ping叩いてプライベートIPでちゃんと通信できとるやんというのに気付き、疑念が晴れる(12:01)。

通信はできるけどmysqlクライアントからの接続だけできんぞという問題の切り分けがようやくできたので、mysqlの設定を疑い、無事、MySQLbind-address オプションが127.0.0.1になっていたのを外すことができました(12:55)。

N+1潰し

僕が2台構成の設定をしている間に、id:karur4n/users/transactions.json のN+1潰しにあたりました。

ベンチ実行 -> failed.

2台構成、N+1潰しが入ったところでベンチを実行。failしました(12:57)。修正に時間かかりそうだったので、一旦ふたりとも昼飯を食うべく休憩に入ります。1時間くらい昼休憩にしてカレーを食べました(14:00)。

ISUCONサーバー環境git設定

そこまでソースの変更をscpでアップロードしてしていたのを、サーバー環境で git pull できるようにするべく、設定をしました。サーバー環境で ssh-keygenしたやつをGithubの設定画面で設定(14:06)。

git clone したけど、publicディレクトリがちゃんとコピーできてなかったりなんやかんやで時間がたっていたが、そんなこんなの間にfailedの修正もid:karur4n が終わらせてくれていました(14:50)。

ベンチ実行 -> 1210

ベンチがパスして、スコア5000だ!!!!やった!!!と喜んでいたら、見ていたのはJob idの方で、実際は1210イスコインでした(14:55ごろ)。

1台構成にしてベンチ実行 -> 900くらい

2台構成にした意味あるんか?という疑問があったので、環境変数変えてlocalhostMySQLにつなぐように戻して再度ベンチ実行。900くらいになったので、よしじゃあ2台にしとこうというやりとりをしました(14:55ごろ)。

ts-node -> nodeへの変更

ExecStart = /home/isucon/local/node/bin/node /home/isucon/isucari/webapp/nodejs/node_modules/.bin/ts-node index.ts になっていたのを、ExecStart = /home/isucon/local/node/bin/node /home/isucon/isucari/webapp/nodejs/node_modules/.bin/ts-node index.js ビルドしたやつを実行するように変更(14:58ごろ)。TypeScriptだとビルドあるからということでそうなったんですが、よく考えるとts-nodeも結局ビルドしてからプロセス立ち上げるのでこれはやらなくてもよかった...?

campaign値の変更

今回のお題はIsucariという架空の椅子専門フリマサイトであり、以下のような仕様になっていました。

POST /initialize のレスポンスにて、イスコイン還元キャンペーンの「還元率の設定」を返すことができます。この還元率によりユーザが増減します。 有効な値は 0 以上 4 以下の整数で 0 の場合はキャンペーン機能が無効になります。

ここまで(スコアだと1210)ではこれをずっと0にしたままだったので、値を変えてベンチを走らせた結果、以下のようになりました(15:16)。

f:id:shgam:20190907233535p:plain

外部サービスのURL取得にDBから引っ張るのやめようとする

アプリケーションから外部APIを叩いている箇所があり、そのAPIのURLがDBに入っていて、それを毎回SQLで取得していました。テーブルには外部APIのURLがこのように保存されていました。

mysql> select * from configs \G;
*************************** 1. row ***************************
name: payment_service_url
 val: https://payment26.isucon9q.catatsuy.org
*************************** 2. row ***************************
name: shipment_service_url
 val: https://shipment26.isucon9q.catatsuy.org
2 rows in set (0.01 sec)

じゃあこれを文字列としてソースコードに書いちゃっていいんじゃないか、と思ってそうしたものの、ベンチがfailするようになってしまいました(15:26)。

GET /users/transactions.json: got response status code 500; expected 200,
POST /buy: got response status code 500; expected 400 (item_id: 50002),
POST /buy: got response status code 500; expected 200 (item_id: 50001)

よくマニュアルを読むベンチマーク走行時にこの値は書き換わる とのこと。残念でした。

外部サービスのURLをキャッシュしようとする

せめてこの値をキャッシュしようとしました。lru-cache というパッケージを追加し、取得の関数でキャッシュを作り、以降はそれを見るように変更しました(16:30)。しかしスコアに改善は見られず...。

getItem

/itesm/:id.json に対応するgetItemという関数がだいぶクエリを発行していたので、これもJOINすればN+1潰せる系かと思い、手を出しました(16:53)。

が、結局悩んでいる間にその修正は間に合わなそうになります(17:21)。

API リクエストが直列になってるの並列にしようとする

id:karu4n の方では、/buy で行われているAPI リクエストを並列にしようとしていました(16:55)。こちらはコミットして変更を反映するものの、スコアに影響は出ませんでした(17:14)。

なぜかfailする

時間も時間なので、そろそろ大きい改善を諦めて調整に入ろうとします。が、ベンチを走らせるとfailedになっていまいました(17:24)。しかしfailedにならず普通にpassするときもあり、よくわからんなとなります。外部サービスのURLをキャッシュするやつがなにかよくない...?と推測してその変更を外して走らせたりしたところ、安定してpassするようになったので外して提出しようと思いますが、最後の方でキャッシュをsetする位置を変えた(/initializeが叩かれたときにキャッシュするようにした)やつで試したところ、passするようだったのでその変更入れました。

ウェブサーバーのmysqlを停止

なんかもう大きなことできないなと思ってつつtopを眺めていると、ときどきmysqldが上に上がってくることがあって、DBサーバーでもないのにこいついらんやろと思ったので、おもむろにウェブサーバーの方で sudo /etc/init.d/mysql stopしときました(18:00ごろ)。

nginxいじる

最後の最後でもう時間もないですが、ぺろっと設定入れて改善するものがあれば入れたいという感じで、 id:karur4n がNginxからgzip圧縮して返すような設定を入れてくれていました。スコアが伸びなかったのでもとに戻してフィニッシュ(18:10)。

感想など

以上は、当日終わったあとにやったことを振り返ってまとめただけのものなので、僕らの勘違いが含まれている可能性もあります。後日講評と解説が公開されたら、それを読みながら再度振り返りをしたいと思います。

こうやってやっていたことを振り返ると、1つ1つ検証せず複数修正を入れて一気にベンチ走らせてスコア伸びたり落ちたりして一喜一憂という場面もあったので、相変わらず雑だったなという反省もあります。

また個人的には、ソースコードを開けば明らかに無駄に発生してそうなDBへの問い合わせは見つかるんですが、それをスピーディに直してベンチ走らせてスコア伸ばすということができておらず、すごい純粋に力不足だったな〜と感じました。そこらへん毎年 id:karur4n のほうができているので、足を引っ張ってしまって申し訳ない。

一方で、一昨年、昨年も出てるので慣れてきたのもあり、やろうとしていたことはそんなに悪くなかったような気がします。マニュアル読んでまずサーバー環境を2台に増やすとか、修正をちゃんとgitで管理/反映するとか、alpでやばそうなところを見つけてそこを直すとか、failしたらログを見て原因を見つける、というようなことはできていたので、そこらへんは例年に比べてバタバタ感が減っていて良かったと思いました。

なにより楽しかったので、また来年も参加してみようと思います。一緒に出てくれた id:karur4n と運営の方々に感謝です。 お疲れさまでした 🍣

デスクトップPWA楽しい

趣味で https://github.com/gaaamii/nekobito というMarkdownエディタをElmで書いているんだけど、久しぶりに開いたら、Chromeアプリとしてインストールできるようになっていた。

f:id:shgam:20190827003521p:plain

ダウンロードされたものを見ると、 app_mode_loader という謎ファイルができているようだ。あとでこれどういうことなのか見てみたい。

どういう仕組みなのかよくわかっていないけどたぶん、以前PWAお試しようと思って雑に置いておいたmanifest.jsonのおかげのような気がする。

なにはともあれ、Elmで書いたものが、ネイティブアプリのように起動できて使える...!!楽しい!!!

趣味開発やっていきたさが高まった。

(追記)デスクトップPWAというらしい。楽しい。

「Elm Meetup in Summer」参加した

書くのが一日遅れましたが、Elm Meetup in Summer - connpassに参加してきました! 楽しかった〜!昨日ちゃんと感想書こうと思ったけど寝てしまったので、いまさらですが箇条書きで書いておきます。

感想

  • 人がたくさんいてElmの勢いを感じた。
  • Elm in Actionという良さそうな本を知ることができてよかった。
  • Fringe81の泉さんの個人開発リポジトリを知れてよかった。Firebaseは自分も趣味開発で使いたいので参考になる。
  • モジュール分割はみんな悩んでそう。
    • モジュール分割の方法が、これだ!っていうのがわかれば大きなアプリでも安心して作れそう。
  • プロダクトで採用してるという人が思ったよりいたけど、Elmはまだメジャーバージョンが0なのに採用できるのけっこう勇気あると思う。どんと来い、breaking change という感じなんだろうか(自分も0.18のときに採用したので人のことを言えない)。
  • Elm普段から使ってる人は割と普通に便利に使ってて、Elm大好き!というよりは淡々とやっていってる感じがあってよかった。
  • jinjorさんのスライドを見ればElmの歴史がわかるので時々見返したい。

仕事のやりがいとは何か?をもう一度観てみた


ずいぶん昔に、まだ働き始める前にこの動画を見た時は、ふーんそうなんだとしか思わなかった。そら褒められた方が仕事楽しくなるよねと。

働き始めてしばらく経ち、動画を見返してみた。動画で強調されているように、大げさなリアクションではなくちょっとしフィードバックだけでもモチベーションに影響があるということが、実感としてわかるようになっていた。

働き出す前に見たときよりも、動画内で語られているちょっとしたフィードバックの大事さが腑に落ちる感じがあって面白かった。

千鳥の「相席食堂」を観始めた

千鳥の「相席食堂」を観始めた。 芸能人が田舎にロケ行って、その土地の人と相席をさせてもらうという番組。 ロケの内容がひたすら雑で、それに対する千鳥のツッコミでめちゃくちゃ笑える。

Amazonプライムビデオのレビューには185件レビューがついていて評価が4.7と高評価ですごい。

「SCRUM BOOT CAMP THE BOOK」を読んだ感想

読んだ。

「基礎編」でスクラムの全体像を説明したあと、「実践編」では漫画でお話が進む。主人公の「ボク」くんがスクラムいいな〜ってぼやいてたら部長にスクラムマスターに任命されて奮闘する感じのストーリーになってる。キャラ設定もだいぶ細かくて面白かった。バッチくんが「バッチ得意です!!!!」と宣言したときは感動ものだった。

個人的には、当初スクラムに対して「ルールがっちり決められて堅苦しい進め方をするやつ」みたいなイメージがあったのだけど、これを読んでだいぶ希望が持てた1。特に現在の自分は実際の仕事でけっこうな失敗2をしたばかりなので、興味深く読めた。

この本に書かれていることを実践するのは面倒だし、やることが増えるように感じる。まずチームをスクラムにするというところから大変だ。

しかし、雑に見積もってえいやで始めて、完璧なソフトウェアが出来上がったりしないことはすでに経験した。

プログラムを書くときに、デザインパターンを利用すれば、「これはDecoratorです」みたいなことが言える。多少のぶれはあっても、自分の新しいアイデアを採用するよりは、認識は揃いやすくなる。それと同じように、スクラムも、チームの中で共通認識として持てることが強いのかなと思った。「どれくらいタスクを消化できるかわからない」となったら、まずベロシティを測るべきだし、「この仕様は誰に聞けばいいのかわからない」となったら、それはプロダクトオーナーに聞くべきだし、「なんかビルド遅いから直したいけどいつやればいいかわからない」となったら、プロダクトバックログに追加して優先度を上げる相談をすればいい。

スクラムのようなものがないとどうなるか?どれくらいタスクをこなせるかもわからず無理なスケジュールを提示してしまうかもしれないし、細かい仕様は開発側でよしなにやってくれということになるかもしれないし、ビルドの改善は残業してやるしかないかもしれない。その問題が、問題と認識されることすらないまま、誰かの頑張りでどうにかするしかないかもしれない。ある瞬間はそれでもいいのかもしれないけど、チームの中でその認識がずれていると、不満が募って、チームのメンバーが退職してしまうかもしれない。

スクラム銀の弾丸どころか、実践が難しくて面倒で堅苦しいものかもしれないけど、複数の人間が共通認識を持つには同じ言葉を使って何度も何度も話し合うことが必要で、スクラムがそのフレームワークなんじゃないかと、そんなことを考えた3


  1. 実際はこの本読む前に id:hiroyuki-hanai さんによる研修も受けさせてもらっていて、それも理解の助けになった。

  2. 幸いリリースはなんとかできたけど、スケジュールは破綻してしまっていた

  3. そんなことはこの本のどこにも書いていないけど

入社して3年経ってた

3年目、ずっとダメダメでしんどかった。今後もエンジニアとしてやっていけるのか不安でしかない。

諸々落ち着いたら休みを取ってゆっくり振り返りたい。

追記

どうぞって...誰に向けてだよ

追記その2

ずいぶんネガティブな感じに書いてしまったけど、休んだら回復してきた。またがんばるぞ。

「CODE COMPLETE」を読んだ感想を書きたい

会社の先輩から勧められて借りてきた。上巻から読んでる。

コンストラクション

この本ではソフトウェアづくりのことをコンストラクションと呼んでる。コンストラクションの範囲のことは語るけどその外のことはこの本の範疇外やでということらしい。コンストラクションの範囲は、ひどいソフトウェア開発だろうがうまいソフトウェア開発だろうが絶対通るような、作る工程のことを指してるっぽい。設計とかは入るけど、顧客折衝とかは入らない感じっぽい。

リーダブルコードだと基本的にコーディングのことばかりだけど、この本はもう少し広い範囲を対象にしてそうというのがわかる。

第5章

第5章は設計の話。設計っていうのはヒューリスティック(発見的)なものであり、決定論的なものじゃないということが書かれている。正解があって、一発で、はいこれねと正解が出せるものではなさそうなのがわかる。

第6章

具体的な話になってきた。ADT(Abstract Data Types)の話。 プログラムのデータ型というとまずstringとかintとかそういうプリミティブなものがあるけど、それをそのまま使うんじゃなくて、現実世界のものを抽象化して表現すると良いぞ、みたいな話。読み進めながら、なるほどオブジェクト指向のクラスの話かと思ったけど、それよりも土台の話らしい。

ADTはクラスの概念の土台となる。クラスをサポートするプログラミング言語では、ADTをそれぞれ専用のクラスとして実装することができる。通常、クラスには他にも継承やポリモーフィズムという概念がある。クラスは「ADT + 継承およびポリモーフィズム」として考えることもできる。

第7章

ルーチンの話。関数(値を返すルーチン)とプロシージャ(値を返さないルーチン)の使い分けなど。C++のマクロの話はあんま関係ないな〜と思って読み飛ばしてしまった。

自分は普段ここで書かれているルーチンの種類を特に区別せず全部関数って呼んでたのだけど、値を返すかどうかによってそうやって言い分けるものなんだ〜というのを学んだりした。

第8章

防御的プログラミングの話。garbage in garbage out (ゴミを入れてゴミを出す)ではだめで、エラーメッセージを出したり、そもそもゴミを入れさせないようにしたりと、ゴミに対処するべきという話。

8.3.1 では堅牢性と正当性という言葉が出てくる。

正当性とは、不正確な結果を決して返さないことを意味する。不正確な結果を返すくらいなら、何も返さない方がましである。堅牢性とは、ソフトウェアの実行を継続できるように手を尽くすことである。

どんなアプリケーションのどんな機能かによって、正当性を優先するか堅牢性を優先するかが変わってくる。正当性を優先すれば、誤ったものがきたときにエラーメッセージを出して処理を中断とかにするだろうし、堅牢性を重視するなら、そうはせずに近い値やデフォルト値みたいなものを変わりに入れて処理を続行したりする。

また、8.4では例外についても触れられている。安易に例外使わないようにしたほうがいいよというスタンスで、こんなことが書かれている。

例外は、予想外の状況に対処する強力な手段と、コードの複雑さの増大とのトレードオフを表す。たとえば、あるルーチンを呼び出すためには、呼び出し元のコードはどこでどの例外がスローされるのかを知らなければならない。したがって、例外はカプセル化を弱め、これによりコードの複雑さが増し、「ソフトウェアの鉄則:複雑さへの対応」にマイナスに働く。

第9章

擬似コードによるプログラミングの話。

第10章

変数の話。なるべく宣言した近くで使おうねという話など。

第11章

第11章は変数の名前の話。その変数が持つ意味を考えて、あとで読んだときに推理しないでもぱっとわかる変数名つけようねという話。

第12章

第12章は基本的なデータ型。浮動小数点数の話とか。


書き途中です。

TypeScriptのkeyofの使いどころ

読者登録したブログ記事をだらだらと巡回していて、こちらの記事を拝見しました。淡々と学んだことをブログに書いていてすごいなと思っていつも読ませてもらっています。

yurufuwa-tech.hatenablog.com

特定のオブジェクトのkeyの値しか許容しない、みたいなパターンの時に使えるって話なんだとは思うのだがこれがどこで使えるのか…というのはちょい謎。。。

TypeScriptは覚えることが多くて自分もあまり自信がないのですが、keyofについてはちょうど最近便利だな〜と感じたことがあったので、傲慢にも勝手にアンサー記事みたいなのを書こうと思いました(間違ってること言ってたらご指摘ください)。釈迦に説法だったら生温かい目で見てください。

Formikのフィールド名に使える

具体的すぎ感ありますが、keyofはFormikのフィールド名を表現するのに使えました。

FormikというのはReactでフォームを扱う際に便利コンポーネントを提供してくれるライブラリです。Formikを利用したフォームは、通常のHTMLフォームと同様、フォームがあって、そのなかにフィールドがあって、それぞれのフィールドにはname属性がついている、みたいな形になります。

const MyInnerForm = (props: Props) => {
  return (
    <Form>
      <Field name="hoge" />
    </Form>
  )
}

Formikを使うと、↑こんな感じでフォーム要素を記述して、 ↓withFormikというHOCで必要なコールバックなどを流し込んだコンポーネントを作れます。

const handleSubmit = (values: Values) => { /* ...省略 */ }
const validate = (values: Values, props: Props)  => { /*...省略 */ }
const MyForm = withFormik({ hanldeSubmit, validate, /* ほかにもいろいろ入れるけど省略 */,  })(MyInnerForm)

Formikは、ユーザーがそのフィールドを触ったかどうかとか、validateで判定したエラーの結果なんかをFormikのpropsとして持っています。

なので、エラーがあるときはこんな形で参照できます。

interface Values {
  hoge: string;
  fuga: string;
  foo: string;
}

const MyInnerForm = (props: Props & FormikProps<Values>) => {
  console.info(props.touched.hoge)
  console.info(props.errors.hoge)

  return (
    <Form>
      <Field name="hoge" />
    </Form>
  )
}

で、場合によってはここで以下のような hasError みたいな関数を定義したくなります(なりました)。 使いやすい、イケてるウェッブサービスをつくりたいので、hasErrorだったときはその場でフィールドを赤くしたりしたくなるのです。

const hasError = (props: Props & FormikProps<Values>): boolean => {
  return props.touched.hoge && props.errors.hoge
}

いいんじゃないでしょうか。しかしこれが複数のフィールドになると面倒です。

const hasErrorOnHoge = (props: Props & FormikProps<Values>): boolean => {
  return props.touched.hoge && props.errors.hoge
}
const hasErrorOnFuga = (props: Props & FormikProps<Values>): boolean => {
  return props.touched.fuga && props.errors.fuga
}
const hasErrorOnFoo = (props: Props & FormikProps<Values>): boolean => {
  return props.touched.foo && props.errors.foo
}

関数1つにしたいですね。フィールド名を渡すようにします。

const hasError = (props: Props & FormikProps<Values>, fieldName: string): boolean => {
  return props.touched[fieldName] && props.errors[fieldName]
}

できた〜。イケてる〜。

となるんですが、問題は、fieldNameの許容する値が広いことです。

hasError(props, 'hogeee')

こうしても、型のエラーにはなりません。stringなのだからそりゃそうです。

ここでようやく、keyof の出番です。これでどうでしょう。

const hasError = (props: Props & FormikProps<Values>, fieldName: keyof Values): boolean => {
  return props.touched[fieldName] && props.errors[fieldName]
}
hasError(props, 'hoge')
hasError(props, 'hogeee') // Argument of type '"hogeee"' is not assignable to parameter of type "hoge" | "fuga" | "foo"

打ち間違えてhogeeeにしたらちゃんと怒られるようになりました。めでたし。

まとめ

TypeScriptほんと難しくてあれやこれや覚えないとな〜という感じで、自分で型書いているときはあまり難しいことはできていないのですが、keyofはわりと出番がありそうだなと思ってます。

関連