プログラマにおける「知識の幅」の重要性

プログラマーの立ち位置として年齢を重ねるごとに求められるのは「知識の幅」なのかもしれない。

プログラマーになりたてに言われた「動くだけのものならだれでも作れる」という言葉の意味を最近よく考える。この言葉は僕の中で的を射ていて、今ウェブサービスを作る人は素人でも5万といるしアプリを作る人も同じようにいっぱいいる。じゃあ、アプリを作れればその人はプロなのかというとそうではなくて、「動くものが作れる」のその後がプロと素人の違いなのかと思う。

Androidに限らず古いバージョンで動くが最新のバージョンだと動かなくなっていたり非推奨になっていたりするものがネットの海には溢れかえっている。モダンな書き方は日々進化するし、便利なライブラリは日々出てくる。言語仕様も変わればIDEも変わっていく。そんな中でこれが一番いいという書き方を見つけるというのは至難の業だし、実際そんな銀の弾丸なコードの書き方なんて存在しないんだろう。使用する言語が変わればコードの書き方が変わるように、一緒にいるメンバーによってコードの書き方も変わった方がいいと思う。

どんな環境でも室の高いコードを書くためには幅の広い知識を得るしか無い。プロジェクトの開始時に記載しだしたコードというのはそのプロジェクトの大きな指針となる。後から加わった人間はその指針にそったコードを書いていく可能性が高く、最初の指針が曲がっていればそのプロジェクトのコードはとんでもない方向へと進んでいくだろう。後から加わった人間の知識の幅が最初にコードを書いた人の知識の幅よりも狭い場合、とんでもない方向に進んでいることすら気づかずにプロジェクトは進んでいき、最終的には挫傷することになるんだろう。

新しいことをやろうとした時も同じだ。

「なにをやりたい」と言われたことに対して、それを実現できるかできないかを判断するのはプログラマーの仕事だ。プロジェクトの核となる「実現すべきこと」であれば調査するのに時間をさいて、実現方法・実現時間を出すのは許されるだろうが、日々の改修での見積もりや実現出来るかの判定はその人間の知識の幅で精度が決まる。知っていれば3分でできることでもプログラマーが無理だと判断すれば、実現すれば有効な施策が実現されないこともあるだろう。出来るかできないかの判定は知識がなければできないのだからここでも知識の幅というのは非常に重要だ。

手を動かしてできることはぶっちゃければだれでも出来る。実現するスピードは違うだろうしメンテナンス性も違うだろう。しかし、「動くだけで良いもの」であればだれでも作れる。その一つ上にたつためにプログラマーは勉強しなければいけないんだろうし、その一歩がプロがプロたる所以になるんだろう。

っと、ここまで書いて思うことは、果たして僕はプロだと胸を張って言えるのだろうかということだ。どれだけの知識を持っていればよいのかに終わりはないし、求められるレベルにもよるんだろうが、どのようなことを聞かれても即座に答えれるプロになってみたいものだ。

SwipeCardに学習結果のリセット機能をつけました!

SwipeCardに要望がありました以下の機能追加・修正をしました。

  • 学習結果のリセット
  • 単語帳で全てのデータを学習完了するとクラッシュする不具合修正
  • 学習が始まっていない状態でテストを開始するとクラッシュする不具合修正
  • その他軽微なバグ修正

引き続きSwipeCardで楽しい学習ライフを送って下さい!

何か要望がありましたらTwitterまでご連絡下さい。

@sathu20xx

タスクの追加や仕様変更は問題ではない

読んだ感想

ほとんどの場合、仕様書の作成者は全てを考慮していなかったことが判明します。それはいつも、設計や開発を始めてから問題が起きて、多くの仕様に問題があるせいだとなってから判明するのです。

作り始めないと見えてこなかった問題が出てくるのはよくある話である。その問題に対する対処が何かの処理を追加することになるのは悪いことではない。問題が出てくればそれに対処するためになにか追加する、もしくは仕様が変更されるのは当たり前の話で作り始めて初めて気づいた問題をそのまま放置するほうがまずいだろう。

では何が問題かといえば、タスクが追加された、もしくは仕様が変更された時点でスケジュールが変更されないことが問題なのだ。

開発者は一番最初に渡された仕様から自分が作るのにかかる時間を見積もってスケジュールを作っている。ここにバッファが入っているかもしれないが、そのバッファはあくまで一番最初に渡された仕様によるバッファだ。仕様変更やタスクの追加に使うのものではない。なので、スケジュールを変更せずにタスクの追加や仕様の変更を依頼するということは、「スケジュールまで作業する時間を増やしてくれ」という依頼を言い方を変えているにすぎない。最悪の場合は「働いている時間が足りないからもっと働け」と言われたと相手に認識されるてもおかしくないのである。

このスケジュールの変更、タスクの追加が定常的に行われだすと、開発者は自分を守るためにバッファを多めに取るようになる。仕様変更やタスクの追加を見越してバッファを増やしてスケジュールを取るようになるのである。そして、開発者が提示してきたスケジュールを見た責任者は前回に比べて作業量が少なく感じ、バッファを大量にとっているのではないかと勘ぐりだす。これによりスケジュール決定時に無駄な腹の探り合いが行われるようになるのである。開発者はこれぐらいしかできない理由を並べ立て、責任者はスケジュールまでにこれをやらないといけない理由を並べ立て、どちらが正しいかを主張しあうのだ。非常に不毛で無駄な話し合いをスケジュールを決める度に行うことになるのである。

相手がある開発はスケジュールが決まるのはしょうがない。ここまでにできていないと準備した広告の意味がなくなる場合や、店舗に発送する時間がなくなるなど色々な迷惑がかかる場合もあるだろう。この場合はしょうがない。スケジュールが伸ばせないのであれば頼むしか無い。なんとか間に合わせるために頑張ってくれと頼むのが筋だ。決してタスクの追加や仕様の変更ができて当然だと考えて言うべきでない。その一言は相手の見積もりが正確ではないといっているのと変わらないのである。

勘違いしてはいけないのは開発者と責任者は敵ではなく味方だ。どちらも良い物を作るために一生懸命作業をしている。そのことを念頭に置いて腹の探り合いなどはせずに、スケジュールを決めたいものである。

MongooseのSlaveからデータを取得して負荷分散する

MongoDBのSlaveからもReadすることで負荷分散しようとしてしらべてみた。

はじめに

MongoDBはDataSyncに時間がかかるので、Readの負荷分散としてSlave使うとデータが遅れる可能性がある。更新が即時反映しないとまずいデータはSlaveを使えない。

種類

Slaveからもデータを取得するようにするにはRead Preferenceを設定すると良い。取得する種別は以下のリンクを参照。

コード

mongooseだと以下のコードのようにかける。

	mongoose.connect(uri, {replset: {readPreference: 'nearest'}});

まとめ

色々なところで嘘が書いてあってなかなかうまく行かなかったけど、上の設定でSlaveに対してもfindのクエリを発行していることが確認できた。しかし、参考にも書いているようにSlaveから取得させるのはそもそものデータ設計が間違っている可能性が高い。indexが効いているクエリを発行出来ているのであればメモリ上にすべてのデータが乗っている場合、データ量がどれだけでかくなろうと速度は変わらないはず。Readで時間がかかっているのはindexが聞いていない可能性が高いのでexplain等を使用してデータ設計、クエリの妥当性を調べ直したほうがいいと思われる。

参考資料

ぼーっとしているようで仕事をしているということ

よくプログラマーは考えるのが仕事で手を動かしてない時でも仕事をしてるとかいう。

でも考えてみるとデザイナーもデザインをひねり出す時間のほうが長いだろうし、プランナーもどういうことをやるかを考える時間のほうがてをうごかしてる時間より長いだろう。なので、ぼーっととしているようでもし仕事をしているっていうのはプログラマーだけじゃなくてどの職業もおなじで、わざわざプログラマーだけをあげるのはバカらしいことなのかもしれないな


ハードキーで音量を変更する

ハードキーで音量を変更しようとすると音を流していない状態だと着信音量が変更されてしまう。

メディアプレイヤーを作った場合、アプリが起動してる時はメディアの音量を変更したいのに着信音量が変更されてしまうのでそれでは使いにくい。

そういった場合は以下のコードを入れるだけでメディア音量を変更することができる。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // こいついれるだけでメディア音量に変わる
        setVolumeControlStream(AudioManager.STREAM_MUSIC);
    }

android studio V1.0でメモリ使用量を変更する

macだと以下に設定ファイルがあるからココを変更する

/Applications/Android\ Studio.app/Contents/bin/studio.vmoptions

ちなみに中身はこんな感じ

-Xms1024m
-Xmx2048m
-XX:MaxPermSize=1024m
-XX:ReservedCodeCacheSize=512m
-XX:+UseCompressedOops

場所は変わってるけど、各内容は以下のURLに記載されている内容と変わらない。

android studioをupdateをしたらandroidannotationが動かない場合の対処

0.9.9に上げたら動かなくなったのでちょっと修正

runProguardは使えない

minifyEnalbedを使いましょう。

// こっちに変える
// runProguard: false;
minifyEnalbed: false;

aptがビルドを通らない

androidManifestを取る時の変数が違う。aptの問題ぽい

apt {
    arguments {
        androidManifestFile variant.outputs[0].processResources.manifestFile
        resourcePackageName 'jp.co.matchingagent.cocotsure'
    }
}

英語学習ソフト「mikan」のandroid版を作ってみた

カードをフリック(スワイプ)して英単語を覚えるアプリとして「mikan」というアプリがある。

英単語を学習するいい感じのアプリを探していたので、早速インストールしようと思ったらAndroid版がない・・・。サイトを見てみると来年の春頃実装される予定らしいのだが、こんな単純なアプリを移植するだけに時間がかかりすぎだろ。。。

来年4月まで待ちたくもないので、無ければ自分で作るしか無いと新しく「SwipeCard」というアプリを作ってみた

「SwipeCard」と「mikan」の違いは以下

  • 10単語ごとに区切ると使いにくいのでエンドレスでカードをめくれるようにした
  • エンドレスでめくれる代わりに忘却曲線を元に効率よく記憶できる仕組みを作った
  • シェアしないと次単語がみれないとかだるいから、全部の単語を最初から見えるようにした。
  • 学習できる単語の種類を増やしてみた
    • 中学生レベル
    • 高校生レベル
    • TOEIC頻出単語

個人的には「mikan」より使いやすいアプリとして完成したんではなかろうかと思っているので宜しければ使ってみてくださいm(_ _)m

PCで作成したsqliteをAndroidのアプリ内にコピーする

読み込んで値を入れていくと遅いのでコピーしてDBファイルを作ったほうよい。isImportDatabase()でfalseが返答された場合dbが作成されてないので、importDatabaseでコピーする。この例の場合はassetsにフォルダにsqliteを入れているsqliteをコピーしている。

    private class DbHelper extends SQLiteOpenHelper {
        private static final String DB_FILE_NAME = "old_db.sqlite3";
        private static final String DB_NAME = "new_db.db";
        private static final int DB_VERSION = 1;

        private Context context;
        private File newDb;

        public DbHelper(Context context) {
            super(context, DB_NAME, null, DB_VERSION);
            this.context = context;
            this.newDb = context.getDatabasePath(DB_NAME);
        }

        public boolean isImportDatabase() {
            return newDb.exists();
        }

        public boolean importDatabase() {
            if (!newDb.exists()) {
                getWritableDatabase().close();
                try {
                    copy(context.getAssets().open(DB_FILE_NAME), new FileOutputStream(newDb));
                    getWritableDatabase().close();
                    return true;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return false;
        }

        private int copy(InputStream input, OutputStream output) throws IOException {
            byte[] buffer = new byte[1024 * 4];
            int count = 0;
            int n = 0;
            while (-1 != (n = input.read(buffer))) {
                output.write(buffer, 0, n);
                count += n;
            }
            return count;
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            super.onOpen(db);
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        }
    }