「クソコード」という表現は好ましくありません
「クソコード」という表現は推奨できません。 なぜなら、コードにクソという言葉をかけることでクソ×クソとなり符号が反転した結果クリーンコードに変化する……ということは期待できないからです。
「みんなではじめるデザイン批評(原著: Discussing Design: Improving Communication and Collaboration through Critique)」という書籍ではデザイン批評でのフィードバックを3つのタイプに分類しています。
反応型フィードバック: 瞬間的かつ反射的、熱がこもっていて個人的な期待や願望、価値観に突き動かされている。直感的な反応。デザインとは関係ない社交辞令もこのタイプ。
「おいおい、ひどいもんだな!酒に酔った犬だって、もうちょっとマシなものを作るよ」「見事ですね!すばらしい出来です!」「あなたの作ったXXXをとても気に入りました」
指示型フィードバック: 助言または提案で始まるが、理由が明示されないことも多い。自身の期待にもっと沿ったデザインにしたいと思っている。
「もし私がするとしたら……」「私なら……」「……だといいのに」 「ラジオボタンは全部ドロップダウンにするべき。なぜなら……」
3.批評(クリティカル・シンキング): 次の3つの要素を含んでいる。
- 分析の対象を明確にする。分析しているのがデザインに含まれるアイディアのどのような側面または判断かを具体的に明らかにする。
- その側面・判断を目的・ベストプラクティスに関連づける。
- その側面・判断が目的・ベストプラクティスにいかに適っているか、いないかについて理由を説明する。
「ユーザーが購入前に自分の銀行残高への影響を真面目に考えることが目的だとしたら、残高を一番下に、他のすべての文字と同じ大きさで表示するのは効果的ではありません。それでは他の情報に紛れてしまうからです」
3番目の批評型が著者の推しで、コードレビューでも好ましいスタイルだと思っています。 2番目の指示型フィードバックについては、理由・根拠の説明がなければもちろん反応型と同じく意味がありません。著者はたとえ理由が説明されていてもよろしくない、と断じていますが、それは批評=分析的思考と問題解決=創造的思考を分けないとミーティングが取っ散らかって収拾がつかなくなると考えているからです。これについては今回の話からは離れてしまうの深追いしませんが、コードレビューなら理由を説明してくれれば問題ないと思います。 1番目の反応型が良くないのは明らかだと思います。コードレビューの場合、レビュアーは潜在的なユーザー・メンテナーであるのでちょっと微妙ですが、具体性がないためフィードバックを活用できないデメリットが大きいです。
本題に戻ります。「クソコード」という表現を使うことは反応型フィードバックの一種であり、「瞬間的かつ反射的、熱がこもっていて個人的な期待や願望、価値観に突き動かされている」ものです。もちろんそのような反応を持つことはとても自然なことなのでその感情を抱くことのものについて非難することはできません。しかし、そのフィードバックを返すことによって対象者が問題を分析をすることや解決をすることの助けになるわけではありません。よって「クソコード」という表現は好ましくないと考えます。
これとは別に「相手に敬意があればそのような表現を投げることはないよね」とか「人格批判ではない感情的になるなと言いつつナマの敵対的感情をぶつけておいて理性的ぶるのは一貫性に欠けているのでは」とも思うのですが、今回はスコープ外です。
2018年振り返りメモ
やったこと
iOS
- 新規アプリ1件(小規模)、新機能追加案件そこそこ、小規模な案件たくさん
- RxSwiftとかMVVMっぽいのとかそれなりにモダンなモノに触れたのは収穫だったし、ViewModelにXCTestを使えたので安定した品質を出せたと思う
- ただ、最近勉強し直すとあまりよろしくない使い方をしていたようだったので、ベストプラクティスには沿えていなかったよう
- RxSwiftとかMVVMっぽいのとかそれなりにモダンなモノに触れたのは収穫だったし、ViewModelにXCTestを使えたので安定した品質を出せたと思う
Android
- 新機能追加案件に2ヶ月+テスト&フィードバックで1ヶ月
- Kotlin100% + MVVM(?) + RxJava/RxKotlin で構成がiOSとほぼ一緒だったので比較的楽に参入できた
Vue.js(Nuxt.js)
- Nuxtに途中参加1ヶ月くらい
- Vue CLI 3 で新規案件2ヶ月くらい
- CSSなにもわからない
- レイアウトはFlexboxファーストでFloatその他は2番手以降に考えていくのが現状では正解っぽい
- VuexとVue Routerが助けてくれたのでネイティブ開発者の知見の枠内で十分に(?)戦えている気がする
- Vuexになんでも突っ込んでたけど流石に危険を感じたので構成を練り直した
- バケツリレーの解決策(特に親→子→孫へのフロー)として使うのはやめにした方がよさそう
- そもそもURLが持つべき情報もあるという考え方がすっぽ抜けていたのはよろしくなかった
- バケツリレーの解決策(特に親→子→孫へのフロー)として使うのはやめにした方がよさそう
- Vuexになんでも突っ込んでたけど流石に危険を感じたので構成を練り直した
- CSSなにもわからない
その他デバイス(Python)
- 出るのかなあ(未公開)
- Python人気らしいけどJSと比べて使いやすいかというとどうなんだろう
雑感
- 去年はiOSでObj-C9割だったことを考えると手を限界まで広げたので満足
- ただ個々のモノを見ていくと専門としては不足している器用貧乏な感じなのが不安
- フロントフルスタックとして生きていくのか/いけるのかというと・・・
- ちょうどWEB+DB Pressのlaisoさんの記事を読んだので、「クライアントサイド何ができるかだいたいわかりますよ(やろうと思えばできますよ)。じゃああなたのやりたいことをどれでやるか考えましょうねー」くらいの動き方は一応できそう。blog.lai.so
- あとはデザインまわりとか。やっぱり画面作りから関われるようになった方がコード書く側としても工数をコントロールし易いし、最終的に使い易いものが作れるはず。たぶん
- テストは最低限どれでも書けないといけないのでAndroid側はもう少し頑張らないと
- フロントフルスタックとして生きていくのか/いけるのかというと・・・
- ただ個々のモノを見ていくと専門としては不足している器用貧乏な感じなのが不安
- FirebaseはAuth/DynamicLink/Analyticsは使っているけどかなり便利なので特にAuthは乗せ替えたい
- Flutterやらないとなあ
iOSDCをきっかけに「Swift5 async/await 研究読本」を購入して読んだ
ツイート
「Swift 5 async/await 研究読本」を キュリオシティソフトウェア書店 で購入しました! https://t.co/rE1Crg010e #booth_pm #iosdc #b
— m (@murakammmmm) 2018年8月31日
async/awaitの本読み終わった! pic.twitter.com/E1hCf4twxd
— m (@murakammmmm) 2018年8月31日
本の中身の前に
- 私はasync/awaitについて以下の事前知識を持っていました
- Swiftでネストの深いクロージャを書き、レビュアーと「これわかりずらいけど素直に書いたらこうするしかないよね」みたいな話をしたことがありました
- @koherさんの Proposalには載っていないSwift 5のasync/awaitが素晴らしいと思う理論的背景 を読んだことがありました
- async/awaitをJavascriptで業務・プライベートで利用していました
- Kotlin, Python, Dartで実装されていて、コード例は目にしたことがありました
- iOSDCで Swiftの生みの親によるasync/await for Swiftを徹底解説し、新しい非同期処理の手法を理解する by 今城 善矩 | プロポーザル | iOSDC Japan 2018 - fortee.jp を見ていました
- スライドにツイートを乗せてくれていて、リアクションをもらえたのが楽しかったのと「( ˘⊖˘) 。o(ここで購入ツイート流したらウケるだろうなあ)」という悪い思い付きによって購入してしまいました
- 先に購入ツイートをした方がいたので二番煎じになってしまいましたが、タイミングがかなり良かったのでウケは良かったです
- せっかく買ったのに読まないのは失礼だろうとの気持ちで分量も多くはない(奥付までで50ページ程度)なので、ざっくり読みました。
本の中身(ざっくり)
第1章 async/awaitとは何か
- ネストの深いクロージャをasync/awaitを利用すること同期的でシンプルなコードとして表現できることについて説明しています。
第2章 Hydraで理解するasync/await
- すでにSwiftでasync/awaitを実装しているライブラリ https://github.com/malcommac/Hydra の使い方についての説明しています。
- Promise単独の例とasync/awaitを併用した例を並べることでasync/await構文がやっていることについて解説しています。
- 確かJavascriptだとPromiseが先に入っていて、後からasync/awaitが追加されていたはずなので、これが自然な導入なんでしょうか。
- エラーハンドリングについてもJavasciprtとおんなじ感じだった記憶があるので、あーそうそうそんな感じだよね、と自然に読めました。初見の人はどうなんだろう。
第3章 Hydraのasync/await実装を読む
- 非同期処理の完了待ちをどうやって既存のSwiftの機能で実装しているかについて説明しています。
- セマフォに尽きる、という印象でした。並行処理の待ちといえばこいつですよね。
最近使ってなかったからこいつの存在を完全に忘れてた複雑怪奇な処理を実装するときにお世話になっていました。 - Promiseのallは存在は知っていましたがzipなんてあったっけ?、と思ってMDNを調べたらjavascriptにはなくてああ動的型付けだからかと解釈しました。
- googleはObj-cで実装したライブラリを公開している( https://github.com/google/promises )。恐ろしい。
第4章 「クリス・ラトナー氏が提案しようとし ている async/await for Swift のプロ ポーザルの下書きを解説する」
- タイトルの通り、今回のasync/awaitの提案(プロポーザル)を和訳した上で説明しています。
- 中身も大事ですがこういう感じで新機能を提案するのか、という意味でも勉強になりました。
雑感
- ツイートもしましたが、すでにKotlin・Javascript・Dartにはasync/awaitが入っているので、今回のSwift5で主要なネイティブアプリの言語でasyncが実装されることになります。Webを含めてフロントエンドプログラマの教養とか基礎知識として理解する必要性が大きいと思っています。
- 会場で質問しましたが、Rxと競合している部分があるので置き換わりが発生すると思っています。通信系のライブラリや画像キャッシュライブラリでReactive Extensionsを作っているようなものは処理をasync/awaitで実装するだろうと見込んでいます。一方でRxCocoaを含むUIとのbinding系は置き換わらない気がするのでそこはRxを続けるかな、と考えています。
今知っていること
- 型
- クラス
- 構造体
- 構造体は値型、クラスは参照型
- SwiftについてはCoWのため別のところに代入しても何か変更を加えなければメモリを消費しないらしい
- 参照型はfor文とかでarrayの中身を取り出して処理していると気づいたら変わっているので好き
- 知らないところで変わっていると死ぬ
- objcはmutable型があったため区別は一応ついたしswiftは基本immutableで進められるから安心感がある
- クロージャは仕事じゃないと絶対に(面倒で)使わなかっただろうけど今はこれなしで処理作れない(arrayの処理をfor文でなくブロック式でやるように指導してくれた先輩には感謝しかない)
- [weak self] は正直なんとかしたい感はある
- 油断するとネストが深くなり「これがコールバック地獄か・・・」という気持ちになれたので早くaysnc await欲しい
- 関数プログラミングはよくわからないけどswiftでmap, reduce, etc. ができたおかげでtotalXXXな変数をmutableで宣言→すぐ下でfor文回すみたいなコードを作らなくてよくなってQOLが上がった。
- main関数は特別
- objcでも実はmain関数から始まっていた(クラッシュしたときに見ることが割とよくあった)。Swiftで最初から作ったアプリだと見ないなあ
- テストは大事
- nil(null)の取り扱いは面倒臭い
- ポインタはわからない
- objcでは雰囲気で使ったけどよく考えたらNSInteger系は値型だったからアスタリスクつけなくても良かったのかな?付けなくてもいいのにNSInteger *intValue にしたせいで不具合になっていた以前の現場を思い出した
- メモリ管理よくわからない
Swiftで旧漢字を判定したかった
できなかった
- 方針その1: shift-jisに変換できるかどうかで判別する
参考: www.softel.co.jp
extension String { func containsOldKanji() -> Bool { return !self.canBeConverted(to: .shiftJIS) } } let str = "高" str.containsOldKanji() // true let str2 = "髙" str2.containsOldKanji() // なぜかtrue
後々の試行錯誤中にshift-jis変換がうまくいっていることが判明し、IBM拡張なるものを知る
文字コード表 シフトJIS(Shift_JIS)
- shift-jis変換は成功しているので、IBM拡張の範囲(纊~黑まで)の漢字が含まれているかどうかを判定する
結果から言うとだめ
extension String { func containsOldKanji() -> Bool { // return !self.canBeConverted(to: .shiftJIS) return containsIBMExtension() } func containsIBMExtension() -> Bool { guard let data = self.data(using: .shiftJIS), let shiftJISStr = String(data: data, encoding: .shiftJIS) else { return false } // http://charset.7jp.net/sjis.html のIBM拡張(?)で漢字相当の範囲を引っ掛ける let regEx = ".*[\u{ed40}-\u{eeec}]+.*" let test = NSPredicate(format: "SELF MATCHES %@", regEx) return test.evaluate(with:shiftJISStr)
上の後でパターンの方もshiftjisにしないとだめかなーとか漢字をダイレクトにパターンに入れてしまえばいいのではないかと思ったがダメだった。
extension String { func containsOldKanji() -> Bool { // return !self.canBeConverted(to: .shiftJIS) return containsIBMExtension() } func containsIBMExtension() -> Bool { guard let data = self.data(using: .shiftJIS), let shiftJISStr = String(data: data, encoding: .shiftJIS) else { return false } // http://charset.7jp.net/sjis.html のIBM拡張(?)で漢字相当の範囲を引っ掛ける let regEx = ".*[纊-黑]+.*" guard let dataRegEx = regEx.data(using: .shiftJIS), let shiftJISRegEx = String(data: dataRegEx, encoding: .shiftJIS) else { return false } let test = NSPredicate(format: "SELF MATCHES %@", shiftJISRegEx) return test.evaluate(with:shiftJISStr)
最後の方は両方ともcontainsOldKanjiがtrueを返していたので「纊-黑」の範囲がshift-jisではなくutf-16とかunicodeとかで解釈されている可能性があり、そもそも何がどうなるとswiftの時代にshift-JISで正規表現を書くのだろうかという気持ちになった。