「クソコード」という表現は好ましくありません

「クソコード」という表現は推奨できません。 なぜなら、コードにクソという言葉をかけることでクソ×クソとなり符号が反転した結果クリーンコードに変化する……ということは期待できないからです。

f:id:slowlylearner:20190816093626j:plain
「みんなではじめるデザイン批評 目的達成のためのコラボレーション&コミュニケーション改善ガイド」第1章より

「みんなではじめるデザイン批評(原著: Discussing Design: Improving Communication and Collaboration through Critique)」という書籍ではデザイン批評でのフィードバックを3つのタイプに分類しています。

  1. 反応型フィードバック: 瞬間的かつ反射的、熱がこもっていて個人的な期待や願望、価値観に突き動かされている。直感的な反応。デザインとは関係ない社交辞令もこのタイプ。

    「おいおい、ひどいもんだな!酒に酔った犬だって、もうちょっとマシなものを作るよ」「見事ですね!すばらしい出来です!」「あなたの作ったXXXをとても気に入りました」

  2. 指示型フィードバック: 助言または提案で始まるが、理由が明示されないことも多い。自身の期待にもっと沿ったデザインにしたいと思っている。

    「もし私がするとしたら……」「私なら……」「……だといいのに」 「ラジオボタンは全部ドロップダウンにするべき。なぜなら……」

3.批評(クリティカル・シンキング): 次の3つの要素を含んでいる。

  • 分析の対象を明確にする。分析しているのがデザインに含まれるアイディアのどのような側面または判断かを具体的に明らかにする。
  • その側面・判断を目的・ベストプラクティスに関連づける。
  • その側面・判断が目的・ベストプラクティスにいかに適っているか、いないかについて理由を説明する。

    「ユーザーが購入前に自分の銀行残高への影響を真面目に考えることが目的だとしたら、残高を一番下に、他のすべての文字と同じ大きさで表示するのは効果的ではありません。それでは他の情報に紛れてしまうからです」

3番目の批評型が著者の推しで、コードレビューでも好ましいスタイルだと思っています。 2番目の指示型フィードバックについては、理由・根拠の説明がなければもちろん反応型と同じく意味がありません。著者はたとえ理由が説明されていてもよろしくない、と断じていますが、それは批評=分析的思考と問題解決=創造的思考を分けないとミーティングが取っ散らかって収拾がつかなくなると考えているからです。これについては今回の話からは離れてしまうの深追いしませんが、コードレビューなら理由を説明してくれれば問題ないと思います。 1番目の反応型が良くないのは明らかだと思います。コードレビューの場合、レビュアーは潜在的なユーザー・メンテナーであるのでちょっと微妙ですが、具体性がないためフィードバックを活用できないデメリットが大きいです。

本題に戻ります。「クソコード」という表現を使うことは反応型フィードバックの一種であり、「瞬間的かつ反射的、熱がこもっていて個人的な期待や願望、価値観に突き動かされている」ものです。もちろんそのような反応を持つことはとても自然なことなのでその感情を抱くことのものについて非難することはできません。しかし、そのフィードバックを返すことによって対象者が問題を分析をすることや解決をすることの助けになるわけではありません。よって「クソコード」という表現は好ましくないと考えます。

これとは別に「相手に敬意があればそのような表現を投げることはないよね」とか「人格批判ではない感情的になるなと言いつつナマの敵対的感情をぶつけておいて理性的ぶるのは一貫性に欠けているのでは」とも思うのですが、今回はスコープ外です。

2018年振り返りメモ

iOSプログラマ3年目。転職1年経過。

やったこと

iOS

  • 新規アプリ1件(小規模)、新機能追加案件そこそこ、小規模な案件たくさん
    • RxSwiftとかMVVMっぽいのとかそれなりにモダンなモノに触れたのは収穫だったし、ViewModelにXCTestを使えたので安定した品質を出せたと思う
      • ただ、最近勉強し直すとあまりよろしくない使い方をしていたようだったので、ベストプラクティスには沿えていなかったよう

Android

  • 新機能追加案件に2ヶ月+テスト&フィードバックで1ヶ月
    • Kotlin100% + MVVM(?) + RxJava/RxKotlin で構成がiOSとほぼ一緒だったので比較的楽に参入できた
      • iOS脳で突っ込んだけど、「あーiOSでいうところの〇〇ね」という気持ちとRxのおかげでだいたいなんとかなった(?)
        • xmlは直にいじれて楽だったけど、appcompatみたいなところは苦戦してた
        • Dagger便利だけどよくわからないまま終わってしまった
      • (そもそも最初から自動テストがなかったから仕方がないというのはあるけど)JUnitAndroid SDKのあれこれをそのまま扱えないのがしんどく、自動テストのカバレッジが壊滅した(UIテストは手が出せなかった)
        • Androidテスト本買えたので立ち向かえるようにしておく

Vue.js(Nuxt.js)

  • Nuxtに途中参加1ヶ月くらい
    • JavaScriptだいたいわかった
    • Vueコンポーネントはネイティブアプリ屋にもわかりやすかった
      • iOSAndroidを両方触っていたので「あーiOS/Andoridでいうところの〇〇ね」という気持ちでなんとかなった
        • 特にAndroidのデータバインディングとVueのテンプレート構文はだいたい一緒という気持ちで進めた(違うけど)
  • Vue CLI 3 で新規案件2ヶ月くらい
    • CSSなにもわからない
      • レイアウトはFlexboxファーストでFloatその他は2番手以降に考えていくのが現状では正解っぽい
    • VuexとVue Routerが助けてくれたのでネイティブ開発者の知見の枠内で十分に(?)戦えている気がする
      • Vuexになんでも突っ込んでたけど流石に危険を感じたので構成を練り直した
        • バケツリレーの解決策(特に親→子→孫へのフロー)として使うのはやめにした方がよさそう
          • そもそもURLが持つべき情報もあるという考え方がすっぽ抜けていたのはよろしくなかった

その他デバイスPython

  • 出るのかなあ(未公開)
  • Python人気らしいけどJSと比べて使いやすいかというとどうなんだろう

雑感

  • 去年はiOSでObj-C9割だったことを考えると手を限界まで広げたので満足
    • ただ個々のモノを見ていくと専門としては不足している器用貧乏な感じなのが不安
      • フロントフルスタックとして生きていくのか/いけるのかというと・・・
        • ちょうどWEB+DB Pressのlaisoさんの記事を読んだので、「クライアントサイド何ができるかだいたいわかりますよ(やろうと思えばできますよ)。じゃああなたのやりたいことをどれでやるか考えましょうねー」くらいの動き方は一応できそう。blog.lai.so
        • あとはデザインまわりとか。やっぱり画面作りから関われるようになった方がコード書く側としても工数をコントロールし易いし、最終的に使い易いものが作れるはず。たぶん
        • テストは最低限どれでも書けないといけないのでAndroid側はもう少し頑張らないと
  • FirebaseはAuth/DynamicLink/Analyticsは使っているけどかなり便利なので特にAuthは乗せ替えたい
  • Flutterやらないとなあ

iOSDCをきっかけに「Swift5 async/await 研究読本」を購入して読んだ

ツイート

本の中身の前に

本の中身(ざっくり)

第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・JavascriptDartにはasync/awaitが入っているので、今回のSwift5で主要なネイティブアプリの言語でasyncが実装されることになります。Webを含めてフロントエンドプログラマの教養とか基礎知識として理解する必要性が大きいと思っています。
  • 会場で質問しましたが、Rxと競合している部分があるので置き換わりが発生すると思っています。通信系のライブラリや画像キャッシュライブラリでReactive Extensionsを作っているようなものは処理をasync/awaitで実装するだろうと見込んでいます。一方でRxCocoaを含むUIとのbinding系は置き換わらない気がするのでそこはRxを続けるかな、と考えています。

今知っていること

  • クラス
    • クラスは[[SomeClass alloc] init] とかSomeClass.init(with: value) みたいなイニシャライザでインスタンスを作る。
    • メソッドにはクラスメソッドとインスタンスメソッドがあり、後者はオブジェクトに備わっている
    • 昔の現場ではmybatis使ってるサーバ側に合わせて単なるマッパーでしかないモデルクラスを作っていて案の定コントローラが肥大し、途中でモデル側に最低限のメソッドを積むようにしていた。
  • 構造体
    • 構造体は値型、クラスは参照型
    • 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で最初から作ったアプリだと見ないなあ
  • テストは大事
    • 最初のところだと単機能の実装に対してexcelで100超えの項目書を作っていたがもうやりたくない
    • XCTest最高!ModelとViewModelなら境界値とシンプルなユースケースは網羅できて幸せ
    • 現実的なUITestってあるのかなあ
  • nil(null)の取り扱いは面倒臭い
    • Optionalは本当に助かってる。nilが入る可能性のない箇所と仕方なく入る箇所が切り分けられるのでコーディングに不安がないし、Javaぬるぽ踏んだので勘弁して欲しい
  • ポインタはわからない
    • objcでは雰囲気で使ったけどよく考えたらNSInteger系は値型だったからアスタリスクつけなくても良かったのかな?付けなくてもいいのにNSInteger *intValue にしたせいで不具合になっていた以前の現場を思い出した
  • メモリ管理よくわからない
    • ARC時代しか知りません(autorelease poolは稀に使った(もう覚えていない))
    • 稀にメモリリークすることがあり、古参の先輩がMRC時代の手動管理の話をしてくれたことがあった
    • objc, Swift はC言語系で参照カウント方式とかJava系はGCがあるという概念の話は聞いている

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拡張なるものを知る
f:id:slowlylearner:20180104033139p:plain
文字コード表 シフト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で正規表現を書くのだろうかという気持ちになった。