株式会社はてなに入社しました
ExifInterface Support Libraryについて調べてみた #関モバ
こんにちは、最近カメラにハマっているid:takuji31です。
なお、この記事の一部は本日開催の関西モバイルアプリ研究会#21で発表された内容です。
Exifについて
ExifはExchangeable image file formatの略で、写真のメタデータを画像に埋め込む仕組みでJPEGやTIFFに使われます。
Exchangeable image file format - Wikipedia
記録内容は
など多岐にわたる一方で、GPSの情報がそのまま記録されることもあり個人情報の漏洩リスクも懸念されています。
最新版のバージョンは2.31で、CIPAのWebサイトから確認できます。
CIPA 一般社団法人カメラ映像機器工業会: CIPA規格類
http://www.cipa.jp/std/documents/j/DC-008-2016-J.pdf
Exif Support Library
Exif Support LibraryはExifの情報の操作を行えるSupport Libraryです。
Support Library 25.1.0から追加されました。
ExifInterafce自体はAPI5からあるのですが、対応しているタグがバージョンごとに違っていたりするので、Support Libraryが用意されたようですね。
対応フォーマット
以下の画像形式に対応しています。
有名どころのファイル形式は一通り対応していますね。なぜかTIFFには対応していなさそうです。
使ってみる
ExifInterface Support Libraryには ExifInterface
というクラスだけが含まれています(正確にはprivateなinnerクラスがいくつか含まれていますが)。
ExifInterface
のインスタンスを作成して、いくつか用意されているメソッドを使って操作する感じですね。
getAttribute
getAttributeInt
getAttributeDouble
それぞれ引数に属性の名前を指定するのですが、名前は定数で用意されています。以下はその一部です。
TAG_DATETIME
TAG_MODEL
TAG_ORIENTATION
TAG_APERTURE_VALUE
TAG_SHUTTER_SPEED_VALUE
TAG_ISO_SPEED_RATINGS
TAG_FOCAL_LENGTH
TAG_GPS_LATITUDE
試しに先日買ったニコンのD500で撮影した写真の情報を抜き出してみました。
- RAWのサムネイルの色がぶっ壊れてる
- 絞り値とシャッター速度が取れない
😇
まとめ
サンプルコードはこちらです。
Atomの自動補完プラグイン「autocomplete-plus」のPerl用Providerを書いている話
これは はてなエンジニアアドベントカレンダー2016 23日目の記事です。
昨日は id:takuya-a さんの文字列アルゴリズムの学びかたでした。
こんにちは、はてなでアプリケーションエンジニアとしてWebサービスやAndroidアプリ(たまにiOSアプリ)を開発しているid:takuji31です。
私は普段VimでPerlを書いているのですが、最近AtomでPerlを書きたくなってAtomの自動補完プラグインである「autocomplete-plus」のPerl用Providerを書いているので、今日はそのことについて話します。
先に謝罪しておきますと、本来はこのエントリーで公開しましたと告知する予定でしたが、まだ実用的なクオリティーには達していないので、開発中とお知らせするだけになります 🙇
Atomとは
Atomとは(恐らくこの記事を読むような方はご存知だとは思いますが)、Githubが公開しているオープンソースのテキストエディターです。
A hackable text editor
とある通り、プラグインを書くことでかなり自由にHackすることができます。
autocomplete-plus
autocomplete-plusはAtomの自動補完プラグインです。
最近のバージョンのAtomに標準でバンドルされていて、インストールするだけで様々な言語のコード補完を行うことができます。
また、標準で用意されている言語以外にも、Providerを用意することで自分でコード補完の候補を作ることができます。
既に存在するProviderの一覧はGitHubのautocomplete-plusのWikiに一覧があります
見てみるとPerl用がないですね、ググってみた感じもないようでしたので、これは作るしかないと思いました。
Providerを作る
雛形を自動生成する
ProviderはAtomのPackageとして作る必要があります。
Atomでは開発用にPackageを生成してくれるメニューがあります、メニューの Packages
-> PackageGenerator
-> Generate Atom Package
を実行しましょう。
コマンドパレットを開いて Package Generator: Generate Package
を探して実行してもよいです。
実行するとPackageの作成先を指定するダイアログが出ますので、適当な名前を決めて入力します。
決定すると指定したパスにサンプルコードと共にPackageが生成されます。
hatena-advent-calendar-2016/ ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── keymaps │ └── hatena-advent-calendar-2016.json ├── lib │ ├── hatena-advent-calendar-2016-view.js │ └── hatena-advent-calendar-2016.js ├── menus │ └── hatena-advent-calendar-2016.json ├── package.json ├── spec │ ├── hatena-advent-calendar-2016-spec.js │ └── hatena-advent-calendar-2016-view-spec.js └── styles └── hatena-advent-calendar-2016.less
以下のファイルとディレクトリはProviderには不要なので消します。
keymaps
- このPackageのキーマップを定義するファイル
menus
- メニューのエントリーを定義するファイル
styles
- Packageのスタイルを定義するファイル
lib/*.js
- サンプルコードですが(Providerでは)一切使わないので消します。
Packageに必要な設定
不要なファイルを整理したら、 package.json
の一番上の階層に以下の設定を追加します。
{ "providedServices": { "autocomplete.provider": { "versions": { "2.0.0": "provide" } } } }
provide
の部分はJS側で定義するメソッド名なので、自由に決めて構いません。
Packageのメインオブジェクトを作る
lib
以下のPackage名と同じjsファイルからexportしたオブジェクトがプラグイン機構の本体になります。
JavaScript以外にもCoffeeScriptも直接使うことができますが、私は業務でTypeScriptを使っていることもあり、TypeScriptで書いています。
/// <reference path="../typings/bundle.d.ts" /> import {Config} from "./config"; import {Provider} from "./provider"; import {UseAndRequireCompletionProvider} from "./use-and-require-completion-provider"; class AutocompletPerlProvider { providers: Provider[] = null config = Config.config activate() { } deactivate() { this.providers = null } provide() { if (this.providers === null) { this.providers = [new UseAndRequireCompletionProvider()]; } return this.providers } } export = new AutocompletPerlProvider()
このように provide
メソッドの中で実際の補完候補を提供するProviderのインスタンスを作って返してやります。Providerは複数指定可能です。
設定項目を追加
メインのオブジェクトに config
フィールドを追加することで、そこに定義されている設定項目がAtomの設定画面に自動的に追加されます。
先ほどのコードでは config
フィールドの中身は Config.config
を参照しているので、その中身を見てみましょう。
class Config { static config = { perlPath: { type: 'string', default: 'perl' }, cartonPath: { type: 'string', default: 'carton' }, useCarton: { type: 'boolean', description: 'Use carton when building completion suggestions. only effects when cpanfile and carton executable exists.', default: false, } } static get perlPath():String { return atom.config.get('autocomplete-perl.perlPath'); } static get cartonPath():String { return atom.config.get('autocomplete-perl.cartonPath'); } static get useCarton():boolean { return atom.config.get('autocomplete-perl.useCarton'); } } export { Config }
上記のコードでは3つの設定項目が指定されています。
このような定義を作成することで、Atomの設定画面にあるPackageの設定項目が自動的に生成されて以下のようになります。
このコードでは設定の定義以外にも、設定値を取り出すのに便利なプロパティーを定義しています。このようにしておくと、実際のコード側で簡単に取り出せて便利でしょう。
Providerを作る
Providerは決まったメソッドとフィールドを持ったクラスである必要があります。
その仕様については、autocomplete-plusのWikiにまとめられています。
Provider API · atom/autocomplete-plus Wiki · GitHub
最低限 selector
フィールドと getSuggestions
メソッドがあればProviderとしては機能します。
/// <reference path="../typings/bundle.d.ts" /> import {Provider, SuggestionInfo} from "./provider"; import {ISuggestion} from "./suggestion"; import {Range, Point} from "atom"; class UseAndRequireCompletionProvider extends Provider { selector : string = ".source.perl" getSuggestions(info : SuggestionInfo) : Promise<ISuggestion> { return new Promise((resolve) => { var suggestions = // ここで補完候補を生成する resolve(suggestions) }); } } export { UseAndRequireCompletionProvider }
atom-autocomplete-perl
では型でこの辺りの制約をはっきりさせておきたかったので、ベースのクラスやインターフェイスを別ファイルで定義しました。
provider.ts
/// <reference path="../typings/bundle.d.ts" /> import {ISuggestion} from './suggestion'; interface SuggestionInfo { editor: AtomCore.IEditor; bufferPosition: TextBuffer.IPoint; scopeDescriptor: AtomCore.ScopeDescriptor; prefix: string; activatedManually: boolean; } abstract class Provider { selector : string = ".source.perl" abstract getSuggestions(info : SuggestionInfo) : Promise<ISuggestion[]> } export { Provider, SuggestionInfo }
suggestion.ts
/// <reference path="../typings/bundle.d.ts" /> type SuggestionType = 'variable'|'constant'|'property'|'value'|'method'|'function'|'class'|'type'|'keyword'|'tag'|'snippet'|'import'|'require'; interface ISuggestion { displayText?: string replacementPrefix?: string type?: SuggestionType; leftLabel?: string leftLabelHTML?: string rightLabel?: string rightLabelHTML?: string className?: string iconHTML?: string description?: string descriptionMoreURL?: string } class TextSuggestion implements ISuggestion { constructor(public snippet: string, public type: SuggestionType) { } } class SnippetSuggestion implements ISuggestion { constructor(public text: string) { } } export { ISuggestion, TextSuggestion, SnippetSuggestion, SuggestionType }
この定義のおかげで、簡単にProviderを増やすことができますね。
あとはひたすら getSuggestions
に渡ってくる入力の情報から非同期に補完候補を生成してresolveするだけです。
とは言うものの、今の時点でまだこれは使っていません、なぜなら簡単なキーワードだけなら自動的に補完候補を生成してくれる便利APIがあるのです。
SymbolProvider Config API
簡単なキーワードなら、SymbolProvider Config API を使うことで補完候補を生成できます。
SymbolProvider Config API · atom/autocomplete-plus Wiki · GitHub
たとえばPerlのビルトイン関数は settings/language-perl.cson
に以下のように書くだけです。
'.source.perl': autocomplete: symbols: builtin: suggestions: [ 'abs' 'accept' 'alarm' 'atan2' 'bind' 'binmode' 'bless' // ... 'waitpid' 'wantarray' 'warn' 'write' 'y' ]
これだけであとは勝手に補完できるようになります。
ビルトイン関数は最初Providerを作って補完できるようにしていましたが、こちらの設定に変えました。
atom-autocomplete-perl の今後について
このProviderは、今のところビルトイン関数の補完だけ動きます。
今後以下のような機能の提供を予定しています。
- package名補完
- クラスメソッド補完
- (構想段階ですが)
Smart::Args
やData::Validator
でバリデーションした変数の型を雑に推測してそれっぽい補完候補を返す
ひとまずpackage名補完だけ追加したら、全国のPerl Mongerの皆様が利用できる状態にしたいなぁと思っています。
まとめ
AtomのPackageは簡単に作ることができました。node.jsを使うことができますので、npmにある大量の資産を活かしたり、他の言語のコードをシステム経由で実行することもできます。
あなたもこの機会にAtomをHackしてみませんか?
はてなではJavaScriptやAltJSが好きなエンジニアを募集しています!
明日の担当は id:tarao です、お楽しみに!
git merge --abort
gitでmergeした時にconflictして、やっぱやめたってなる時に今までは git reset --hard HEAD
ってしてたんだけど、ふとmanを眺めたら --abort
オプションがあるのを発見した。
rebase
とか cherry-pick
にあるのになんでないのって思ってたけど、どうも勘違いだったっぽい。
gitちゃんと使うならもっとオプション知らないといけないなぁと思った。
TODOコメントにGitのbranch名を入れたい続編
以前TODOコメントにGitのbranch名を入れるためにSnippet書いたという記事を書いた。
この記事以降もチームは変わってないし使っているのだが、branch名そのままだとそのbranchのmergeまでに終わらせないといけないTODOに思えるという指摘があった。
たしかにそうだなって感じだったので、branch名のルールを少し見直して feature-name/part
みたいな感じにすることにした。
それに合わせてsnippetのコードも少し改変。
snippet todo options head # TODO(`substitute(substitute(substitute(system('git rev-parse --abbrev-ref HEAD'), '\n\+$', '', ''), '^\(feature|hotfix|release|design\)\/', '', ''), '\/.\+$', '', '')`): ${0}
git-flow で使いそうなprefixとか design/
とか を取っ払ってやることでとにかくそれっぽい名前を出すことに成功した。
最近PerlをAtomで書けるように準備しているので、このsnippetもそのうち使わなくなりそうだけども、とりあえず書き残しておく。
Kotlinの演算子オーバーロードについて
これは Kotlin Advent Calendar 2016 9日目の記事です。
今日はKotlinの演算子オーバーロードについて紹介します。
演算子オーバーロードとは
演算を行うための記号が演算子です、だいたいのブログラミング言語にはありますよね?
+ - / * =
や && ||
など、プログラミング言語にはたくさんの演算子があります。
Kotlinでは様々なクラスを演算子で演算した時の挙動をコントロールする演算子オーバーロードの機能があります。
これはJavaにはない機能ですね。
Kotlinの演算子オーバーロードの仕組み
Kotlinで演算子により演算を行う場合、左辺のクラスに存在する演算子に対応した名前で右辺の型にマッチするメソッドが存在した場合に行われます。
1 + 2 // 3
+
は plus
メソッドを呼ぶので以下と同じ意味になります。
1.plus(2)
実際にKotlinで数値リテラルのメソッドを呼ぼうとすると、補完候補に plus
や他の演算子に対応したメソッドが出てきます。
ボウリングで学ぶ演算子オーバーロード
決まった名前でメソッドを作るだけです。これは通常のメソッド以外に拡張関数でも可能です。
私はボウリングが趣味なので、ボウリングのスコアを表現するクラスを作ります。
ボウリングは雑に説明すると、10本のピンに向かってボールを転がしていっぱい倒したらハッピーというスポーツです。
10本のピンは2回以内に倒しきらないといけなくて、1投目で倒しきったらストライク、2投目で倒しきったらスペア、倒しきれなかったらミス(エラー)となります。
ストライク、スペア、ミスのどれかで投げ終わる1つの流れをフレームといい、これを9回繰り返します。
9回繰り返した次の10フレーム目は最大3投でき、3投するかミスをした場合に終わります。
この10フレームが1ゲームになります。
クラスを定義
まずはスコアそのものを表現するクラスを作りましょう。
object Strike : BaseScore(value = 10) object Spare : BaseScore(value = 0) class Score(value: Int) : BaseScore(value = value) // ↓複雑になるので一旦は考慮しないことにします // object Foul : Score(value = 0) // object Miss : BaseScore(value = 0) // object Gutter : BaseScore(value = 0) // class Split(value: Int) : Score(value = value) }
スペアは10-(1投目の投数)ですが、便宜的にオブジェクトで表現します。
次にフレームを作ります、9フレーム目までのフレームは1〜2つのスコアからなります
複雑度を下げるために、フレームは完了しないと計算できないようにします。
sealed class Frame(val scores: List<BaseScore>) { object StrikeFrame : Frame(listOf(Strike)) class SpareFrame(firstScore: BaseScore) : Frame(listOf(firstScore, Spare)) class MissFrame(firstScore: BaseScore, secondScore: BaseScore) : Frame(listOf(firstScore, secondScore)) } class TenFrame private constructor(val scores:List<BaseScore>) { constructor(firstStrike: Strike, secondStrike: Strike, thirdScore: BaseScore) : this(listOf(firstStrike, secondStrike, thirdScore)) constructor(strike: Strike, secondScore: Score, thirdScore: BaseScore) : this(listOf(strike, secondScore, thirdScore)) constructor(firstScore: BaseScore, spare: Spare, thirdScore: BaseScore) : this(listOf(firstScore, spare, thirdScore)) constructor(firstScore: BaseScore, missScore: Score) : this(listOf(firstScore, missScore)) }
10フレーム目だけ途中で投げ終わる可能性があるので、起こり得る全パターン(ただし3投目は何でもいいので固定)をコンストラクターで用意しました。
フレームが集まればゲームになります、ゲームを表現するクラスを作りましょう。
sealed class Game(val frames: List<Frame>) { class IncompletedGame(frames: List<Frame>) : Game(frames) class CompletedGame(frames: List<Frame>, tenFrame: TenFrame) : Game(frames) }
本当は初期家事にフレーム数のバリデーションが必要ですが、今回は雑にこういう感じでいきます。
スコア + スコア = フレーム
さて、これを演算できるように実装していきましょう。
Score(1) + Score(8) // ミス Score(1) + Spare // スペア
まずは↑のパターンを作ります
open class Score(value: Int) : BaseScore(value = value) { open operator fun plus(b: BaseScore): MissFrame { return if (this.value + b.value < 10) { MissFrame(this, b) } else { error("Invalid score combination!") } } operator fun plus(spare: Spare): SpareFrame { return SpareFrame(this) } }
この組み合わせで1つのフレームができあがります。
ストライク + スコア = フレーム2つ (完了していないゲーム)
ストライクだけは1つでフレームが終わってしまうので、ちょっと特殊です
Strike + Score(3) // ストライクと3ピン倒した状態、今回これは考慮しない Strike + Strike // ダブル Strike + (Score(3) + Spare) // ストライクとスペア
これは Strike オブジェクトに演算子オーバーロードをしましょう。
object Strike : BaseScore(value = 10) { operator fun plus(strike: Strike): IncompletedGame { return IncompletedGame(listOf(StrikeFrame, StrikeFrame)) } operator fun plus(frame: Frame) : IncompletedGame { return IncompletedGame(listOf(StrikeFrame, frame)) } }
これだけで大丈夫です、Strikeは1つでフレームになるので、演算時にStrikeFrameに暗黙的に変換しているような扱いにしています。
フレーム + フレーム = 完了していないゲーム
フレームが2つ以上集まると完了していないゲームになりますね。ストライク単体もフレームと同じ扱いです。
sealed class Frame(val scores: List<BaseScore>) { operator fun plus(b: Frame): IncompletedGame { return IncompletedGame(listOf(this, b)) } operator fun plus(strike: Strike): IncompletedGame { return IncompletedGame(listOf(this, StrikeFrame)) } }
単純ですね。
完了していないゲーム + フレーム = 完了していないゲーム
完了していないゲームにどんどんフレームを足していきます、最終的には9つまで許容します。
class IncompletedGame(frames: List<Frame>) : Game(frames) { operator fun plus(b: Frame): IncompletedGame { return if (this.frames.size == 9) { error("Next frame is TenFrame!") } else { IncompletedGame(frames + b) } } operator fun plus(strike: Strike): IncompletedGame { return this + StrikeFrame } }
10個目を足そうとするとエラーになります。
完了していないゲーム + テンフレーム = 完了したゲーム
ここまでくるとほぼ完成ですね、10フレーム目を足しましょう。
class IncompletedGame(frames: List<Frame>) : Game(frames) { operator fun plus(b: TenFrame): CompletedGame { return if (this.frames.size != 9) { error("Next frame is not TenFrame!") } else { CompletedGame(frames, b) } } }
使ってみる
ここまできたら、1ゲームを表現する演算ができるようになるはずです。
実際に先日投げたこの3ゲームをKotlinで表現してみましょう。
3ゲーム目ェ… pic.twitter.com/KaIFZVj48G
— たくじ@ToSジェミナ鯖 (@takuji31) December 6, 2016
1ゲーム目
Strike + Strike + (Score(9) + Score(0)) + (Score(7) + Spare) + Strike + (Score(9) + Spare) + (Score(7) + Score(1)) + Strike + Strike + TenFrame(Strike, Score(8))
2ゲーム目
(Score(7) + Spare) + Strike + (Score(9) + Spare) + Strike + (Score(9) + Spare) + (Score(9) + Score(0)) + (Score(9) + Spare) + (Score(9) + Spare) + (Score(8) + Spare) + TenFrame(Score(9), Spare, Score(7))
3ゲーム目
Strike + (Score(9) + Spare) + Strike + (Score(7) + Score(0)) + (Score(9) + Score(0)) + (Score(8) + Score(0)) + Strike + Strike + (Score(7) + Score(1)) + TenFrame(Strike, Score(9), Spare)
全部エラーなしに書けました!
実際のところ、入力のバリデーションが必要だったりするので、これで完成ではないですが、演算子オーバーロードとしてはこれで完成かなと思います。
まとめ
いかがでしたでしょうか、当たり前ですが右辺の型で戻す型を変えたりなどできるので、結構柔軟な計算ができるようになります。
これ以外にも、中置関数を使うと組み込み演算子以外のカスタム演算子として使うことができるようになります。
詳しくは公式ドキュメントをご覧ください。
最後に
計算まで終わらなかったのでそのうち実装して公開します。
以下が今回のコードの全容です。
sealed class BaseScore constructor(val value: Int) { object Strike : BaseScore(value = 10) { operator fun plus(strike: Strike): IncompletedGame { return IncompletedGame(listOf(StrikeFrame, StrikeFrame)) } operator fun plus(frame: Frame) : IncompletedGame { return IncompletedGame(listOf(StrikeFrame, frame)) } } object Spare : BaseScore(value = 0) class Score(value: Int) : BaseScore(value = value) { operator fun plus(b: Score): MissFrame { return if (this.value + b.value < 10) { MissFrame(this, b) } else { error("Invalid score combination!") } } operator fun plus(spare: Spare): SpareFrame { return SpareFrame(this) } } // object Foul : Score(value = 0) // object Miss : BaseScore(value = 0) // object Gutter : BaseScore(value = 0) // class Split(value: Int) : Score(value = value) } sealed class Frame(val scores: List<BaseScore>) { operator fun plus(b: Frame): IncompletedGame { return IncompletedGame(listOf(this, b)) } operator fun plus(strike: Strike): IncompletedGame { return IncompletedGame(listOf(this, StrikeFrame)) } object StrikeFrame : Frame(listOf(Strike)) class SpareFrame(firstScore: BaseScore) : Frame(listOf(firstScore, Spare)) class MissFrame(firstScore: BaseScore, secondScore: BaseScore) : Frame(listOf(firstScore, secondScore)) } class TenFrame private constructor(val scores:List<BaseScore>) { constructor(firstStrike: Strike, secondStrike: Strike, thirdScore: BaseScore) : this(listOf(firstStrike, secondStrike, thirdScore)) constructor(strike: Strike, secondScore: Score, thirdScore: BaseScore) : this(listOf(strike, secondScore, thirdScore)) constructor(firstScore: BaseScore, spare: Spare, thirdScore: BaseScore) : this(listOf(firstScore, spare, thirdScore)) constructor(firstScore: BaseScore, missScore: Score) : this(listOf(firstScore, missScore)) } sealed class Game(val frames: List<Frame>) { class IncompletedGame(frames: List<Frame>) : Game(frames) { operator fun plus(b: Frame): IncompletedGame { return if (this.frames.size == 9) { error("Next frame is TenFrame!") } else { IncompletedGame(frames + b) } } operator fun plus(b: TenFrame): CompletedGame { return if (this.frames.size != 9) { error("Next frame is not TenFrame!") } else { CompletedGame(frames, b) } } operator fun plus(strike: Strike): IncompletedGame { return this + StrikeFrame } } class CompletedGame(frames: List<Frame>, val tenFrame: TenFrame) : Game(frames) } class Main { companion object { fun main(args: Array<String>) { Score(1) + Score(8) // ミス Score(0) + Score(10) // スペア Score(2) + Score(8) // スペア Strike + Strike + (Score(9) + Score(0)) + (Score(7) + Spare) + Strike + (Score(9) + Spare) + (Score(7) + Score(1)) + Strike + Strike + TenFrame(Strike, Score(8)) (Score(7) + Spare) + Strike + (Score(9) + Spare) + Strike + (Score(9) + Spare) + (Score(9) + Score(0)) + (Score(9) + Spare) + (Score(9) + Spare) + (Score(8) + Spare) + TenFrame(Score(9), Spare, Score(7)) Strike + (Score(9) + Spare) + Strike + (Score(7) + Score(0)) + (Score(9) + Score(0)) + (Score(8) + Score(0)) + Strike + Strike + (Score(7) + Score(1)) + TenFrame(Strike, Score(9), Spare) } } }
転職に対して思っていること色々
これは 転職(その2) Advent Calendar 2016 2日目の記事です。
こんにちは、株式会社はてなでアプリケーションエンジニアをやっているid:takuji31です。
最初はそろそろ転職して1年になるのでその辺の話を書こうかと思ったんですが、ちょっと思うところがあって内容を変えました、理由は以下の通りです。
転職して給料上がって時間も増えてレベルの高い人達と一緒に仕事できて会社にも不満なくてとにかく最高って話転職Advent Calendarに書こうかと思ったけどただの自慢話になりそう
— たくじ31(歳) (@takuji31) 2016年11月30日
はっきりこれといったテーマがない感じなので雑多にメモっぽいこと書きます。
自分の価値観なので、それは違うよってことあるかもしれませんし、行き詰まってる人の参考になるかもしれませんし、他の方の考え方を否定するものではありませんのであしからずご了承ください。
経歴的なもの
1987年3月1日生まれの29歳、大阪市生まれ大阪市育ちの大阪市民で2010年に4年制の専門学校を1年休んで5年で卒業して、東京のモバイル系のベンチャーに新卒入社。
1年半ほどソシャゲの開発と運用をやって、半年ほどスマートフォンの着メロサイトの開発と運用をやっていた。
同時に入社してすぐの頃からAndroidアプリの開発も並行してやっていて、その年の夏に(仕事では)自分にとっても自社にとっても初めてのAndroidアプリをリリースした。
ちょうど2年経った頃、元同僚の誘いで某アパレル系大企業の子会社に入社、日本でトップ3以内にいる某ファッションECサイトのスマートフォンアプリを作ったりなどしていた、iOS開発をやり始めたのはこのタイミング。
色々あって1年ほどで大阪の実家に帰ることになって転職、前職に入社。スマートフォンアプリの受託開発を経験。
半年で辞めようと思ったけどそろそろ5年くらいはどこか1社で頑張った経歴が欲しいと思いつつも2年半ほどで身体の方にガタがきて転職活動を始めて、以前からとある方にお誘いいただいていた縁もあって、今年の1月からはてなに入社した。
転職理由を振り返ってみる
振り返ってみるとポジティブともネガティブな理由ともとれそうな感じの転職の仕方をしてきていて、実際そうだと思う。
会社への不満とか自分がここにいて本当に成長できるのかとか、目指しているものが違うとかそういう思いがありつつ、一方でステップアップしたいみたいなことは常に思っているし、すごい人達と仕事したいからそういう人達いるところを探したいみたいなのもある。
はてなに入ってみて11ヶ月経って思うけど、やっぱりすごい人達の集まりって感じだし、YAPCとかで登壇してて何となく雲の上とは言わなくても自分とは全然違う世界にいそうだと思っていた人達と今仕事をしているのがとても良い。
何をもって転職失敗とするのか
※たぶんフィクションです
- 年収が2/3くらいになる
- 残業時間激増する
- 仕事が無駄にキツくなる
- 入った会社のサービスに興味がない
- その他労働環境の悪化
- やりたかったことと違うことやってる
- 年齢層が自分と合ってない(めっちゃ年寄りばかりとか、若すぎるとか)
- 会社に通いづらい
実際はてなに入った時も「通勤時間長くて朝つらい(京阪経由で1時間40分、JRでも1時間20分ほど)」問題があったが、今は京都に引っ越して解決している。
この辺は独身だと解決しやすい問題という気がしているが、共働きとか子持ちだと大変そう。
転職失敗しそうな要素
全部自分の話
- 次の会社より辞める日が先に決まっている
- 辞める理由がはっきりしていない、とにかくつらいとかそういう気持ちになってる時
- 他人(主にその時所属している会社の人事とか)に言われて転職を決めた時
- Uターンで選択肢が少ない(分からない)時
- ちゃんと色んな選択肢を見ずに言われたまま決めた時
転職を成功させる確率が上がるかもしれない要素
自分の話かもしれないし、想像かもしれない
- 自分自身のスキルを常に磨く
- 業界のトレンドを追う
- 他社の人とつながっておく
- 同僚にも自分の実力を分かってもらっておく
- コネクションがないなら転職エージェントにも相談する
年収
多くの人が転職の理由の一つに加える要素の一つだと思う。
一応自分は2回目の転職以外は基本的に上がってるし、残業代やボーナス入った実績値なら一度も下がってないんだけど、オファー時の提示額が下がるの結構モチベに影響ある気がしている。
少なくとも同じような職種に転職する時は上がっておきたい。
その他転職に対して思うこと雑多に
- 給与交渉で弱気になって下げるとだいたいロクなことない
- 転職回数、あまり気にしない方がよいのではないか、回数気にして落とされるなら縁がなかったと思えばよさそう
- 残業代実際見込みじゃないけど見込みの計算で提示される年収、実際に計算するとめちゃくちゃ安くなって面白い
- プログラマーの給料、関東と関西で月額5万円くらいのギャップがあるらしいがソースは忘れた
- 残業ほとんど0に近かった人間が残業時間40時間くらいの会社に行くと周りとの感覚のギャップに悩まされるのでオススメできない
- 結局は縁だと思うので、いいと思ってた会社落ちたからといって自分はゴミクズなのでどこの会社からも必要とされていないとか思わない方がよさそう
- 縁だけでなくタイミングもあるので、一度落ちた(と思う)会社を再度受けて入社できることはある、実は前回の転職の時にはてな受けて書類で落ちていた(エージェントから連絡が来なかったので実際はどうか曖昧だけど)
- 一方で前回書類で落ちた別の会社にもう1回書類送ったけどまた落ちた、冷静に考えて経験と一致してないしまぁ普通にあるよねって感じ
まとめ
準備とかちゃんとして転職成功しようぜ!