Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

魂のコーディング

1,980 views

Published on

コードに想いを込めよう!

読みやすく分かりやすいプログラムを書くためのエッセンスを説明しています。

Published in: Engineering
  • Be the first to comment

魂のコーディング

  1. 1. コードに想いを込めて! bb.tech  #03  LT資料   (株)アイキューブドシステムズ   田崎 大輔   2015/07/17
  2. 2. p  経歴   p  2005〜2008:  女子大非常勤講師   p  2008〜2014:  組み込み系エンジニア、アーキテクト   p  2014〜        :  i3Systems,  inc.  @  福岡   p  Ruby/Rails/インフラエンジニア   p  ソーシャル   p  t:  @devchick99   p  f:  daisuke.tasaki.7 ※本資料は私自身の見解であり、必ずしも私の所属する組織、立場、戦略、意見を代表するものではありません。
  3. 3. p  i3Systemsが自社開発するモバイルデバイス管理システム   p  Ruby  on  Rails製   p  モデル数:  356個   p  コントローラ数:  294個   p  モバイルデバイスをリモートで運用/管理   p  デバイス情報の取得・閲覧   p  デバイス機能の設定・制限   p  ネットワーク設定の一括配信   p  アプリ配布ポータル   p  デバイスのロック、データ消去   p  モバイル管理(MDM/MAM/MCM)市場4年連続シェアNo.1(※) ※出典:  「ミック経済研究所・ITリポート2014年12月号」    「ミック経済研究所・コラボレーション/コンテンツ・モバイル管理パッケージソフトの市場展望  2013年度版」    2011年度、2012年度、2013年度出荷金額実績および2014年度出荷金額予測
  4. 4. ※以下、Rubyでサンプルコードを書いていますが、話の本質的にはCでもJavaでも言語問わず通用する内容です(と信じています)。
  5. 5. #  コマンドが有効なバージョンかチェックする def  enable_app_version?(app_version,  command_app_version)      if  ApplicationModel.check_ver_isnew(app_version,  command_app_version)  ||            app_version.to_f  ==  command_app_version.to_f          return  true      else          return  false      end   end
  6. 6. p  check_ver_isnewメソッドはおそらく新しかったら trueを返すのだろう   p  でもどっち!?  どっちが新しいとき?  同値だったらどうなんの?   p  その後のto_fしてるのはなに?  小数点込みはダメなん!? #  コマンドが有効なバージョンかチェックする def  enable_app_version?(app_version,  command_app_version)      if  ApplicationModel.check_ver_isnew(app_version,  command_app_version)  ||            app_version.to_f  ==  command_app_version.to_f          return  true      else          return  false      end   end
  7. 7.        if  tmp_new[idx].nil?              rtn  =  false              break          end            str_new  =  tmp_new[idx].rjust(3,  '0')          str_old  =  tmp_old[idx].rjust(3,  '0')          if  str_new  >  str_old              rtn  =  true              break          elsif  str_new  <  str_old              rtn  =  false              break          end      end      rtn   end p  → 定義元もなにしているのかわからん  (-­‐”-­‐; #  バージョンチェック   def  self.check_ver_isnew(new,  old)      tmp_new  =  new.split('.')      tmp_old  =  old.split('.')        if  tmp_new.length  >=  tmp_old.length          max_size  =  tmp_new.length      else          max_size  =  tmp_old.length      end        rtn  =  false      for  idx  in  0..(max_size-­‐1)          if  tmp_old[idx].nil?              rtn  =  true              break          end  
  8. 8. p  “1.0.0”のような文字列2つを引数にとり、   新しいバージョンか調べるメソッド   p  第1引数の方が第2引数より新しいバージョン番号の場合true   p  同値の場合はfalse   p  同値も含めたい場合は呼び出し後に同値チェック(to_fの箇所)   p  ただし、“1.0”と“1.0.1”を同値と認識してしまう不具合あり   p  そんなん読み取れないよ!   p  この挙動を完全に把握するのに約30分かかりました…   p  もっとコードに想いを込めようぜ!!   check_ver_isnew(“1.0.0”,  “0.9.9”)  →  true   check_ver_isnew(“1.0.0”,  “1.0.0”)  ||  “1.0.0”.to_f  ==  “1.0.0”.to_f  →  false  ||  true   check_ver_isnew(“1.0.0”,  “1.0.1”)  ||  “1.0.0”.to_f  ==  “1.0.1”.to_f  →  false  ||  true!?  
  9. 9. p  ISO/IEC  9126   p  ソフトウェア品質特性に関する国際規格   p  機能性:  機能とその特性に影響する特性群   p  信頼性:  ある状況がある時間続いたときにどの程度機能するかに影響する特性群   p  使用性:  利用するのにかかる手間、個人の努力などに影響する特性群   p  効率性:  ソフトウェアの性能やそれに要するリソース量に影響する特性群   p  保守性:  何らかの変更を加えるのにかかる手間に影響する特性群   p  移植性:  別の環境にソフトウェアを移行させる可能性に影響する特性群   p コードに想いを込めて保守性向上   p  作成/変更の意図を誰が見てもわかるように  
  10. 10. p  保守性が必要ないケース   p  自分しか使わないコード   p  一度しか使わないコード   p  保守性が重要なケース   p  他人も見るコード   p  ずっと使われ続けるコード   p  なぜなら   p  我々は製品コードを作成&メンテするプロ集団だから。   p  チームでコードを作成し、   p  サービスがEOLを迎えるまでメンテし続けなければならない とにかく動けばなんてもいい
  11. 11. #  バージョンチェック #  使い方1:   #      ApplicationModel.check_ver_isnew(比較対象文字列1,  比較対象文字列2)   #          →  比較対象文字列1  が 比較対象文字列2  より新しい場合true,  同値もしくはより古い場合false   #  使い方2:   #      ApplicationModel.check_ver_isnew(比較対象文字列1,  :newer_than  =>  比較対象文字列2)   #          →  比較対象文字列1  が 比較対象文字列2  より新しい場合true,  同値もしくはより古い場合false   #  使い方3:   #      ApplicationModel.check_ver_isnew(比較対象文字列1,  :newer_than_or_equal_to  =>  比較対象文字列2)   #          →  比較対象文字列1  が 比較対象文字列2  より新しいもしくは同値の場合true,  より古い場合false   def  self.check_ver_isnew(  *args  )      ……      #  初期値:  等号含むならtrue,  含まないならfalse      #  (各要素が同値なら次の要素へ判断を持ち越す。すべて同値なら判断できず、初期値のままとなる。) result  =  (equality  ==  :newer_than_or_equal_to)  ?  true  :  false      new.zip(old).each  do  |str_new,  str_old|          #  文字数の多い方に合わせて右寄せでゼロ埋め          str_len  =  [str_new.length,  str_old.length].max          ……      end   end
  12. 12. p  メソッドの使い方説明を追加   p  2つの引数のどっちが新しいときにtrueとなるのか明記   p  同値の場合にtrueとなるのか明記   p  既存の仕様はそのまま   p  どうせならメソッド名も変えたかった…   p  同値を含めるか否かを呼び出し時に指定できるように   p  説明的なハッシュキー&バリューで指定   p  複雑な処理にはコメントを追加   p  なぜこのコードが必要かという理由も  
  13. 13. p  変数名をより説明的に   p  例:  この変数はオブジェクトのときと配列のときとどっちもあるんだよな…   p  その想いをそのまま変数名にしたらいい!  →  device_or_ids    #  サービスへの招待をコマンドプッシュで通知する def  invite_by_command(admin,  device_or_ids)        if  device_or_ids.is_a?(Device)            device_ids  =  [device.id]        elsif  device_or_ids.is_a?(Array)            device_ids  =  device_or_ids        end        ……    end  
  14. 14. p  短命な変数は1文字でもOK。ただし意味深く。   p  イテレータ変数iはindexを意味する   p  tmpは非推奨   p  ローカル変数なんて大抵テンポラリなもの   p  何を表現したいのかもっとよくよく考えよう   p  長命なほどより意味深く、より説明的な命名を 長命 短命 for文の   イテレータ変数 ローカル変数 メソッドの仮引数 インスタンス変数 グローバル変数
  15. 15. p  メソッドを呼ぶことでどんなことが起きるのかわかるように   p  英語のSVC構文、SVO構文を意識する   p  短く略称を使ってもよいケース   p  汎用的に、頻繁に呼び出すメソッド   p  気軽に(副作用なく)利用できるメソッド     p  意味深く説明的にするケース   p  特定の用途にしか使わないメソッド   p  呼び出しに注意が必要(副作用がある)メソッド    app_ver.newer_than_or_equal_to?(command_ver)        →  Is  the  app_version  newer_than_or_equal_to  command_version?    wizard.validate_params_on_building_request        →  The  wizard  validates  parameters  on  building  a  request.      name  =  device.to_s      self.generate_aes_encrypt_compatible_key    ←  単にgenerate_keyだったらどんな用途かわからない      self.push_certificate_with_disable_expired_certificates  
  16. 16. p  クラス名はグローバル定数とほぼ同義   p  長命   p  ヒト/モノ、用途を表す   p  ヒト/モノ:  User、Device   p  用途:  DeviceBuilder、CommandGenerator   p  業界用語を使う   p  製品ドメインの用語   p  デザインパターン名   p  コードの作者と読者で共通の認識を持ちやすい   p  ただし、目的と手段を履き違えないように
  17. 17. p  コメントは作者から読者への直接的なメッセージ   p  でも、変数名/メソッド名/クラス名が充分に意味深ければコメントは不要   p  効果的なコメントとは?   p  whatは今のコードを読めば大体わかる   p  how/whyでコーディング時の想いを残そう   p  [how]  クラス/メソッドの使い方説明   p  [why]  なぜそのコードが必要なのか   p  [why]  なぜ現在のコードになったのか    #  結果を返す      return  result      #  初期値:  等号含むならtrue,  含まないならfalse      #  (各要素が同値なら次の要素へ判断を持ち越す。すべて同値なら判断できず、初期値のままとなる。)    result  =  (equality  ==  :newer_than_or_equal_to)  ?  true  :  false      #  人物がいない場合は、該当組織のみで検索実施      #(不具合対応。これをはずすと人物が存在しない組織の管理者の場合に条件が追加されない)    #  return  {}  if  people_abstract_user_ids.blank?      abstract_user_ids  =  organization_abstract_user_ids  +  people_abstract_user_ids   論外
  18. 18. p  publicなメソッド   p  いろんなところから呼び出されている可能性があるため全体 grepして影響調査が必要   p  privateなメソッド   p  そのクラス内だけで使われているため、クラス外部への影響を 気にしなくて良い   p  なので、   p  とりあえずpublicで。というのはやめよう   p  影響範囲はここまでなんです!と主張しよう
  19. 19. p  利用者と拡張者へのメッセージ   p  to  利用者:   p  オブジェクトがどの具象クラスのもの か分からなくても、そのメソッドは呼 び出していいよ   p  to  拡張者:   p  子クラスを実装するときには必ずオー バーライドしなさい   p  Rubyにはvirtual修飾子がない   p  どのメソッドでも後から気軽に拡張 できるから疎かにされがち   p  親クラスのメソッドで例外発生させ ることで表現 class  Device      def  push_notification          raise  “Must  be  overriden!”      end   end  
  20. 20. p  コミットメッセージで改修意図を示そう   p  [what/when/where/who]  コミット内容を見れば大体わかる   p  [why]  なぜその改修をしたのかを残そう   p  [how]  どうやったかは残しにくい   p  ログ表示やblame表示したときに読まれることがある   p  プッシュしてしまうとメッセージを修正できない   p  誤字/脱字、関連するチケット番号に間違いがあるかも   p  情報としてちょっと信頼性が低いので注意
  21. 21. p  コード改修の意図を記載しよう   p  コメント、コミットメッセージには書ききれなかった改修意図   p  思いの丈をレビュアーにぶつけよう   p  コード改修が問題ないことをどう確認したか   p  [how&result]  影響範囲調査項目とその結果   p  [how&result]  デバッグ試験項目とその結果   p  コードだけでなくプロセスのレビューができるように  
  22. 22. p  コードからチケットへのトレーサビリティ   p  コメント、コミットメッセージにチケット番号を記載   p  チケット番号を自動的にリンクしてくれるツールを活用   p  Redmine、JIRA&Bitbucket、などなど   p  チケットにはいろいろ書ける   p  [what]  改修内容の概要   p  [how&result]  影響範囲調査項目とその結果  (プルリクがなければ)   p  [how&result]  デバッグ試験項目とその結果  (プルリクがなければ)   p  [why]  コード改修のそもそもの要件
  23. 23. p  実装の意図大事!   p  なぜそのコードなのか意図を主張しよう   p  名前付け大事!   p  意味深く説明的な名前がついていればコメントはもはや不要   p  コードに込め切れないあふれた感情は   p  コミット、プルリク、チケットに吐き出そう

×