TONY0922のブログ

学んだことを適当に記録していくブログです。主にRubyで仕事してます。最近はJavaScriptとObjectibe-C始めました。Titanium関連もちょいちょい触ってます。更新頻度はそんなに高くないので、ご了承下さい。

事業会社に転職して半年経ったので、ふりかえり

今年の頭に中小SIerから自社サービスを展開する事業会社に転職をして、 半年以上経ったので、振り返えりを書こうと思う。

なんで転職したか?

Sierで受託開発の仕事をしている間に 個人で小さなWebサービスをいくつか作った事があり、 それを周りの人につかってもらったり、そのサービスを大きくしていく上で、 次第に自分でWebサービスをグロースしていく仕事を 本職にしたい気持ちが強くなり、転職を考えるようになった。

結果、とある事業会社に内定をもらったので、転職した。

事業会社に転職してどうだったか?

「技術はあくまで手段であって、売上にコミットできないエンジニアはいらない。」 最初に上司に言われた言葉がこれだった。

恥ずかしい話だが、入社する前は企画サイドが考えた施策を 開発サイドが実現可否やスケジュールを判断し、 自社メンバーで開発を進めていくものだと思っていた。

つまり、プロジェクト(以下、PJ)の売上を伸ばす施策は企画サイドが考え、 開発サイドはあくまでシステム開発を、いかにスピード感を持って、進めていくかにコミットするものと思っていた。

が、実際には違った。

まず、エンジニアもPJの売上のKPIのどこかにコミットする必要があり、 その上でどんな機能を実装すれば、売上が上がりそうかをプロダクトオーナー、 もしくはプロジェクトリーダーに提案をし、開発を進めていくフローになっていた。

なので、PJ内の色んなデータを集めて、分析して、どこに売上が上がる余地があるかを見つけ、 提案資料を作成して、合意を得て、やっと要件定義や開発に入れるわけだ。 ただ、基本的には開発は社内常駐の業務委託の方にお願いすることがほとんどなので、 実際に自分で手を動かして、実装する機会がほとんどなく、エクセルやパワポを使う日々がここ暫く続いている。 (もちろんコードレビューやちょっとしたスクリプトを書いたりはする。)

なので、最近は技術書よりかは、自分でPJの課題を見つけて施策を提案するために必要な 「仮説思考」やら「ロジカルシンキング」やら「定量分析」などのビジネス書を読む機会が増えている。

まぁ、今後は自分の進め方次第では、自分で企画した案件を自分で実装できそうなので、 早く企画を通して、実装を楽しみたい。

気が向いたら、もうちょっと追記するかも...

Go事始め

転職先で使うことになったので、勉強を初めてみた。 とりあえず、以下のサイトが良いらしいので、ここから初めて見る。

A Tour of Go

それが終わったら、下記の記事を見ていきたい。

ASCII.jp:Goならわかるシステムプログラミング

3回目の転職活動で利用した転職サービスの雑感

転職(その2) Advent Calendar 2016 11日目の記事です。

久しぶりに転職活動を行ったので、その雑感を書いていきます。

筆者のスペック

RubyJavaでシステムの受託開発がメインで要件定義 〜 開発まで一通りやらせてもらった。
いままでは受託オンリーの仕事をしており、事業会社でのエンジニア経験も積みたかったので、転職を決めた。

転職エージェントは使えるか?

使おうかどうか迷ってたが、結局今回も2社ほどお願いした。

1社は今の会社の転職でもお世話になった転職サービス大手のI社だ。
もう一社はヒカリエに本社を構えるエンジニア転職に特化したR社である。

R社に関しては、エンジニア転職をメインに行っていると聞き、技術的なところも理解した上で色んな会社を紹介してくれると期待したが、エージェントの技術的な知識が皆無で、I社にくらべて、R社を利用するメリットが無いと感じた。

そんなわけで最終的にR社は切ってしまった。

後、今回は年収を上げることも目的としてあったのだが、これに関してはどちらのエージェントも態度が消極的だった印象がある。
本音を言うと、年収upの戦略や助言みたいなのも期待していたので、残念だった。

転職ドラフトも使ってみたよ。

https://job-draft.jp/

これも3回くらいドラフトに参加した。
ありがたいことに色んな企業から面接のオファーを頂くことも出来た。
このサービスは自分の経歴を企業側が読んで、予め想定年収を提示してくれるため、終盤のメンドクサイ年収交渉をある程度スキップできるところが非常に良かった。
興味がある会社から面接オファーが来ても、提示年収が低ければ、断ることもできるので、余計な労力を使わずに済んだのは非常にありがたかった。

また、ある会社が他のエンジニアにどれくらいの年収でオファーを出すのかも分かるので、その会社の年収相場みたいなものも分かるのが良かった。
これにより、興味はあるが、年収相場があまりにも低い企業を発見できたりと、色々調査できるのが面白い。

最後に

最終的にエージェントを通して、転職が決まったが、個人的には転職ドラフトは大きくなってほしいサービスだと思っているので、次、もし転職する機会があれば、また使ってみたいとは思っている。

iTerm + tmux 使用時にvimの色設定が反映されない。

iTrem と tmux 使用時にvimrcで設定した色設定が反映されなかった時は、 下記の設定を行うと良い。

zshを使用しているが、bashでも同じと思われる。

alias tmux="TERM=screen-256color-bce tmux"
set -g default-terminal "xterm"

最後に反映を忘れずに。

$ source ~/.zshrc

参照URLはこちら

【Objective-C】翌日の指定時間のNSDateを取得する。

翌日の指定時間を取得したかったので、色々調べた結果 下記のような感じで書けそう。

NSDate* date = [NSDate date];
NSCalendar *calendar = [NSCalendar currentCalendar];

NSDateComponents *dateComps = [calendar components:NSYearCalendarUnit |
                               NSMonthCalendarUnit  |
                               NSDayCalendarUnit    |
                               NSHourCalendarUnit   |
                               NSMinuteCalendarUnit |
                               NSSecondCalendarUnit fromDate:date];
// 例)翌日の20:00を取得したい。
[dateComps setDay:(dateComps.day + 1)];
[dateComps setHour:20];
[dateComps setMinute:0];
[dateComps setSecond:0];

NSDate * notification_date = [calendar dateFromComponents:dateComps];
NSLog(@"date: %@", notification_date);
date: 2014-06-19 11:00:00 +0000

リファクタリング:Rubyエディション を読んだ。 〜 6章まで

リファクタリング:Rubyエディション

リファクタリング:Rubyエディション

久しぶりにリファクタリングについて勉強したくなったので、 読んで、重要と思った部分をメモ書きする。

第1章 リファクタリング

一時変数をメソッドリファクタリングする際にパフォーマンスの低下が懸念されるかもしれない。(P41)

  • そんなに問題にならないケースが多い。
  • できるだけ、リファクタリングは保守性を重視して、読みやすく変更を加えてやるべき。
  • 一時変数は無用に多くの引数をやりとりする原因になる点でない方が良い。
  • 一時変数を取り除けば、コードが何を使用としているのかがはっきりと分かるようになる。

※ 実際は一時変数をメソッドに置き換えて、リファクタリングをする人がどれくらいいるのか調べてみても良いかもしれない。

リファクタリングの問題点

インターフェースを公表済みの場合

  • 公表済みのインターフェースをリファクタリングをするのは、それの周知にコストがかかる。
  • したがって、普通は旧インターフェースと新インターフェースを両存させる。

第6章 メソッドの構成

メソッドの抽出

コードの断片をメソッドにして、その目的を説明する名前をつける。

利点

  • メソッドの粒度を細かくできれば、再利用しやすくなる。
  • 高水準のメソッドがコメントの連続のような感じで読みやすくなる。

Tips

  • コメントは抽出できるメソッドを見分けるのに使えることが多い。また、コメント自体が抽出されたメソッドの名前の候補になる。

一時変数から問い合わせメソッド

式をメソッドにする。一時変数の全ての参照箇所を式に置き換える。新しいメソッドは他のメソッドからも使える。

利点

  • 一時変数はローカルスコープのため、アクセスしようとするとメソッドを長くする以外に方法がない。したがって、問い合わせメソッドに置き換えれば、クラス内の全てのメソッドからアクセスできるようになる。

一時変数からチェインへ

チェイニングをサポートするようなメソッドに書き換え、一時変数を不要にする。

利点

  • 不要な一時変数を削除できる。
  • 自然に読めるようにコードを組み合わせられるインターフェースが増えるので、保守性が向上する。

Tips

  • チェイニングできるようにしたいメソッドからはselfを返すように実装する。
  • 一つのオブジェクト内でチェインは完結させたほうが良い。そうすることによって、表現力を高めることができる。

説明用変数の導入

処理の目的を説明するような名前を持つ一時変数に式、または式の一部の結果を代入する。

利点

  • 複雑な条件式が読みにくい場合は一時変数を使えば、式を管理しやすい形に分解出来る。

Tips

  • 一時変数はあくまでそのメソッド内のコンテキストでしか使えないため、できるだけメソッドの抽出を利用した方が汎用性は高い。

一時変数の分割

代入毎に別々の一時変数を用意する。

利点

  • ループ変数や結果蓄積用変数でないにもかかわらず、同じコンテキスト内で同名変数を使用すると、読み手が混乱するため、必ず別々の変数名を用意すること。

メソッドからメソッドオブジェクトへ

メソッドの抽出」ができないようなローカル変数の使い方をしている長いメソッドがあるとして、メソッドを独自のオブジェクトに変え、全てのローカル変数がそのオブジェクトのインスタンス変数になるようにする。こうすれば、メソッドを分解して、同じオブジェクトの別のメソッドにすることが可能になる。

利点

  • ローカル変数はメソッドオブジェクトの属性になり、このオブジェクトを対象として「メソッドの抽出」を行うと、元のメソッド分解して小さなメソッドを作ることが可能になる。
  • 作成したオブジェクトのメソッドに処理を委譲することで、引数渡しの心配をせずにメソッドの抽出が可能になる。

ループからコレクションクロージャメソッド

ループではなく、コレクションクロージャメソッドを使う。

利点

  • コレクションの中を行き来したり、派生コレクションを作ったりするためのインフラストラクチャコードを隠し、ビジネスロジックに集中できるようにしてくれる。

Tips

managerOfiices = employees.select {|e| e.manager? }.
                            collect{|e| e.office }
  • 合計の計算のように、ループ内で一つの値を生み出すような処理をしなくてはいけない時には、
    injectメソッドを使用する。
total = employees.inject(0) {|sum, e| sum + e.salary}

サンドイッチメソッドの抽出

重複部分を抽出して、ブロック付きのメソッドにする。この種のメソッドは呼び出し元に一時的に制御を返してくる。呼び出し元は、制御を返された時に実行するコードをブロック内に記述する。

利点

  • 前後の重複部分を抽出してメソッドにまとめることができる。
  • インフラストラクチャコード(コネクションを反復処理するためのコード)を隠して、ビジネスロジックを全面に押し出すことができる。
    • つまり、公開メソッド内に保つことができるのでメンテナンスがしやすくなる。

クラスアノテーションの導入

クラス定義からクラスメソッドを呼び出してふるまいを宣言する。

利点

  • 宣言的な構文でコードの目的が明確につかめる場合には、「クラスアノテーションの導入」を使うと、コードの意図を明確にできる。
  • 有名な例としては、属性アクセッサが挙げられる。

Tips

  • 様々なクラスで使いまわせそうであれば、Moduleとして抽出して、Classクラスに移した方が良い。

名前付き引数の導入

引数リストをハッシュに変換し、ハッシュキーを引数の名前として使う。

利点

  • 他のオブジェクトに処理を移譲をしようする際に、渡す引数の役割が明確になっていれば(公開インターフェースが明確な振る舞いをきちんと表現していれば)、移譲先のオブジェクトの詳細を深く見なくても済む。

Tips

  • アサーションの導入すると、下記2点の利点がある。
    • 渡さなければいけない引数の種類の明確化
    • キーの綴りの間違い時の検出
module AssertValidKeys
    def assert_valid_keys(*valid_keys)
        unknown_keys = keys - [valid_keys].flatten
        if unknown_keys.any?
            raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(",")}")
        end
    end

Hash.send(:include, AssertValidKeys) # Hashオブジェクトを拡張する。

Class Books
    def self.find(selector, hash={})

        # ここでhashに渡すキーの種類が確認できる。
        # 誤ったキーが与えられるとArgument Error が発生する。
        hash.assert_valid_keys :conditions, :joins

# 以下、省略
Books.find(:all)
Books.find(:first, :conditions => "authors.name = 'Jerry James'", :joins => [:authors])
  • 呼び出し元のコードが分かりやすくなるという利点がある一方で、呼び出されるメソッドが複雑になるコストもかかってしまうので、呼び出されるメソッドをそこまで複雑にする必要性がない場合は、名前付き引数を取り除く
    • 引数の数が減ったり、オプション引数の一部が必須引数になったりして、Hashの名前付き引数から、必須引数を取り除いた場合
    • メソッドの抽出」や「クラスの抽出」を行って、元の引数から一つだけ呼び出して引数とするようなメソッドやクラスを作った場合

動的メソッド定義

メソッドを動的に定義する。

利点

  • 読みやすくメンテナンスし易い形式でメソッド定義を簡潔に表現できる。

Tips

  • 単純なメソッドの繰り返しであれば下記のようなメソッドを定義しておくと便利である。
class Class
    def def_each(*method_names, $block)
        method_names.each do |method_name|
            define_method method_name do
                instance_exec method_name, &block
            end
        end
    end
end
def_each :failure, :error do |method_name|
    self.state = mathod_name
end
class Post
    def self.states(*args)
        args.each do |arg|
            define_method arg do
                self.state = arg
            end
        end
    end

    states :failure, :error, :success
end
  • 動的に定義されたモジュールをextendしてメソッドを定義する。
class to_module
    def to_module
        hash = self
        Module.new.do
            hash.each_pair do |key, value|
                define_method key do
                    value
                end
            end
        end
    end
end
class PostData
    def initialize(post_data)
        self.extend post_data.to_module
    end
end

動的レセプタから動的メソッド定義へ

動的メソッドを利用して必要なメソッドを定義する。

利点

  • method_missingを利用したクラスのデバッグは悲惨になりやすく、最悪の場合スタックレベルが深くなりすぎてエラーになる。オブジェクトがどのように使われているかわかっているケースではmethod_missingを使わずに振る舞いを実現できる。

Tips

  • method_missingを使わない動的な移譲
    • 無効なメソッド呼び出しはデコレータのNoMethodErrorsとして正しく報告される。
    • method_missing定義もないため、スタックレベルが深くなりすぎることもない。
class Decorator
    def initialize(subject)
        subject.public_methods(false).each do |meth|
            (class << self; self; end).class_eval do
                define_method meth do |*args|
                    subject.send meth, *args
                end
            end
        end
    end
end

属性の中にnilのものがあるかどうかを判定する。

class Person
    def self.attrs_with_empty_predicate(*args)
        attr_accessor *args

        args.each do |attribute|
            define_method "empty_#{attribute}?" do
                self.send(attribute).nil?
            end
        end
    end

    attrs_with_empty_predicate :name, :age
end

動的レセプタの分離

新しいクラスを導入し、method_missingのロジックをそのクラスに移す。

利点

  • 新しいクラスにメソッドmethod_missingを移動させることによって、method_missingを利用することで発生するデメリット(予想外のNoMethodErrorの頻発やSystemStackErrorを発生)を抑えることができる。

Tips

  • 有効なメソッド呼び出しは予めわかっていることが非常に多い。その場合は「動的レセプタから動的メソッド定義へ」を使ったほうが良い。

Gitポケットリファレンスを読んだ

Gitポケットリファレンス

Gitポケットリファレンス

プロジェクトでGitを使う機会が増えてきて、 通常の業務で困ることがなくなったけど、 もうワンランク使いこなせるようになりたいなーと思ったので、 読んでみた。

以下、勉強になったコマンドを張り付けていく。


git config

Git の各種設定を表示、変更する。

以下、興味をもった設定のみ記載。

commit.template

コミットメッセージのテンプレートをファイルで指定する。

core.whitespace

git diffなどで不正な空白文字を検出して通知する。

push.default

引数なしでgit pushされた際に、どのブランチにプッシュするか指定する。


git commit

インデックスに登録した変更内容をローカルリポジトリに反映する。

-a

バージョン管理している全変更をコミットする。

-c

コミットメッセージを以前のコミットやタグから読み込む

--dry-run

コミット結果を表示。実行はしない


git add

変更内容をインデックスに登録して、コミット対象にする。

-p

パッチ形式で確認しながら1つずつインデックスに登録する。

-A

バージョン管理対象になっていないファイルも含め、すべてをインデックスに登録する。

-n

addコマンドの実行結果を確認する。

-i

変更内容をファイルずつ確認しながら、インデックスるに登録する。


git rm

バージョン管理しているファイルを削除して、バージョン管理の対象外とする。

-- cached ファイルを残したまま、バージョン管理対象からファイルを削除する。


git reset

インデックスに登録したファイルをコミット対象から外します。

-p

インデックスに登録された変更箇所をバッチ形式で1つずつ選択して、インデックスから取り除きます。

--hard HEAD^

直前のコミットの一つ前の状態にソースを戻す。

--hardを間違えて実行した場合

reflogを使う。


git revert

コミットした内容を取り消すコミットを実行する。

-m parent-number

マージコミットを取り消す際にどのブランチをメインラインとして残すかを指定する。

--no-edit

revert用に自動生成されるコミットメッセージをそのまま採用する場合に指定する。

-n(--no-commit)

revert内容のコミットを自動で行わない。


git pull

共有リポジトリ(リモートリポジトリ)から変更を取得し、現在のブランチにマージします。

--rebase

変更を取得した後、rebaseを行う。

--no-ff

必ず、マージコミットを行う。明示的にマージしたコミットを残す際に利用する。

--squash

リモートブランチの複数のコミットを一つのコミットにまとめてブランチに取り込む。


git fetch

共有リポジトリ上のリモートブランチの変更を取得する。

-p

取得後に存在しなかったリモート追跡ブランチを削除する。

--dry-run

取得結果を確認する。取得結果の反映はしない。


git push

ローカルブランチへの変更を共有リポジトリへ送信します。

-u

新規に作成したリポジトリやブランチをプッシュする際に、ローカルリポジトリのブランチとプッシュ先リポジトリのブランチの対応付けを行う。

--mirror

リモートリポジトリにローカルリポジトリの内容をそのまま複製したいときに使用する。


git remote

リモートリポジトリを一覧表示、追加、削除、変更をします。

-t branch

pullコマンドで更新内容を取得するリモートリポジトリを限定できる。

remote prune

リモート名からリモートリポジトリで削除済のブランチをローカルリポジトリから削除します。


git branch

--no-merged

カレントブランチにマージされていないブランチを表示する。

--merged

マージ済みブランチを表示

デフォルトでは共有リポジトリのタグやブランチをどのユーザでも消せてしまうので、共有リポジトリにフックを設定することでそれを防ぐことが可能。

git checkout

-b

ブランチを作成して、切り替える。

-t

リモート追跡ブランチからブランチ作成をする前に、作成するブランチにアップストリーム設定をする。 ※ アップストリーム設定とはpushやpull時の指定を省略できる。

-p

パッチ形式で確認しながら1つずつコミット時点のものに戻す。


git tag

-a

注釈付きのタグを作成する。

-s

署名付きのタグを作成する。


git archive

配布用のファイルを作成する。


git merge

対象ブランチの変更を現在のブランチに取り込む

--no-ff

マージコミットを必ず生成する。

--log

マージコミットのコミットメッセージにマージされるコミット郡の一行目のコミットメッセージを含める。

--no-commit

マージ後、自動的に行われるコミットを行わない。

競合発生時でファイル単位で解決を行う場合

git checkout MERGE_HEAD XXXX.txt git checkout ORIG_HEAD XXXX.txt で解決できる。

Merge時にFast-forwardマージとコミットマージのどちらを採用すればよいか?

ウォータフォール的にかっちりとした開発モデルのプロジェクトの場合はどのブランチで作業して、いつマージしたかを分かりやすく把握するためにコミットマージを使用する。開発者が分散していて、自由度の高いOSSプロジェクトの場合は、「履歴を一直線に保って見やすくする」方針を取ることが多い。


git rebase

コミット履歴を変更して、ブランチの分岐元を置き換えることができる。また、コミットの順番の入れ替え等、コミット履歴を編集できる。

--onto []

ブランチが「どこのブランチから分岐したか」を修正します。実行後は、から分岐してからののコミット郡が、--onto で指定したブランチから分岐したように修正される。

rebase | --continue | --skip | --abort

rebase実行中に、競合が発生した場合、競合を解決してrebaseを続けるか、競合が発生したコミットをスキップしてrebaseを続けるか、rebaseを取りやめて、rebase実行前の状態に戻るか選択する。

--continue

競合解決中にリベースを再開したい場合に使用する。

--skip

競合解決中にそのコミットをスキップしたいときに使う。スキップしたコミットは適用されない。

--abort

競合解決中にリベースを中止し、リベース開始前の状態に戻りたい場合に使用する。

--squash

複数のコミットを一つにまとめる。


git log

現在のブランチのコミットメッセージを表示します。

--graph

コミットの履歴をグラフで表示する。

--color

出力を色付けする。

--left--right

2つのブランチ間のコミットの差を表示する場合に使用する。

--reverse

逆順に表示する。(古いコミットから順に表示する。)


git diff

インデックスに未登録の内容や次のコミットで反映される内容を表示します。

--cached

特定のコミットとインデックス間の差分を表示する。

-w

行同士を比較するときに、すべての空白文字を無視して比較する。

-b

行末の差分を無視して、差分を表示する。


git show

コミットの差分、もしくはファイルの内容を表示する。

--oneline

コミットメッセージを1行で表示する。


git blame

ファイルの特定の場所がどのコミットで変更されたかを参照できる。

-L

表示する行の範囲を指定する。

-M

同じファイル内でコピー&ペーストした行を検出し、元となるコミットを表示する。


git reflog

リポジトリの操作履歴を確認できる。


git svn

SubversionリポジトリをGitで操作する。
Subversionを共有リポジトリに使用していても、ローカルではGitを使用できる。