にゃみかんてっくろぐ

猫か百合を見守る壁になりたい

死活監視をFirebase Realtime Databaseでお手軽にやってみる

死活監視といえば,たとえば MackerelUptime Robot などが使えます.

ただし,こうしたサービスは「エージェントをインストールする」か「外部からサービスにアクセスできる」必要があるほか,無料版に制限があるなど少々ネックがあります.

そこで,Firebase Realtime Databaseで死活監視チャレンジしてみました.

監視対象の条件

  • インターネットに到達できる(外部公開は不要)
  • cron または 任意の定期実行可能なジョブ管理システム がある
  • cURL または 任意のHTTPクライアント がある

監視対象: REST APIを定期的に叩く

監視対象のホストから,定期的にハートビートを送信します. データの保存  |  Firebase に記載されている Realtime Database の REST API を用います.

crontab -eでcrontabに定義を追加します.

*/10 * * * * curl -X PUT -d "\"`date '+\%Y-\%m-\%d \%H:\%M'`\"" https://examples.firebaseio.com/heartbeats/`hostname`.json

Realtime Databaseにデータが追加されます.

f:id:no_clock:20190603232659p:plain
Firebase Realtime Databaseのようす

アラート: Cloud FunctionsからSlackに通知する

アラートも作ります.Cloud Functionsで定期実行するようにします.

Node.jsですが普段あまり書かないのでクオリティはお察しです.

export const scheduledFunction = functions.pubsub.schedule('every 5 minutes').onRun((context) => {
  request.get({
    url: "https://examples.firebaseio.com/heartbeats.json",
  }, function(error: any, response: any, body: any) {
    const info = JSON.parse(body)
    for (let hostname in info) {
      const diff = Date.now() - Date.parse(`${info[hostname]}+09:00`)
      if (diff < 1000 * 60 * 30) { continue; }

      const options = {
        url: 'https://hooks.slack.com/services/<fugafuga>',
        form: `payload={ "text": "${hostname}: ${info[hostname]} 以降 heartbeatがありません." }`,
        json: true
      }
      request.post(options)
    }
  });
  return 1
});

ためしにしきい値を1分とかにして毎分実行させると,ちゃんとSlackに通知されます.

f:id:no_clock:20190603235821p:plain
ゆかりん

注意事項

おわりに

Realtime Databaseとは… という感じですが,Realtime Databaseは課金対象がストレージとトラフィックのみで,リクエスト数に制限がないため,少量のデータを高頻度にやり取りするケースによく合います.もっとも,今回の規模であればFirestoreでも無料枠で済みますが…

めでたしめでたし.

SIerから自社サービス系に転職して半年ちょっと経過した

2018年9月にSIerから自社サービス企業に転職した.会社の宣伝ではないので社名は伏せる(たどれば出るが).

退職エントリは書かなかったが,半年ちょっと経ったので考えを整理してみようとポエムにした.

学生時代

小学生でVisual Basic 6.0に触れ,中学生でソフトウェアを公開して自宅サーバも立てていた.今も続いている.

SIer時代

就職活動は熱心に行わなかった.「親を安心させたかった」というもっともらしい理由で大手グループのSIerに入った.

世の中には不気味なSIerのdis記事があふれていて,残念なことに私がかかわった現場の大半もその通りだった.

メモリは4GBで,Eclipseに統合された謎の静的コード解析ツールはファイル保存のたびに1分間フリーズした.3年前の話で,今は違うかもしれない.

なぜそんな闇が残り続けるのかというと,私は「効率化するメリットが(会社に)ないから」と勝手に解釈している.効率化してもお金にならない,そんな不思議な世界だったように思う.

何を得たか

と言っても,無だったわけではない.得たものはあったし,転職して初めて「あの経験役立ってる」と気づくこともあった.

  • 品質意識とバランス感覚.バグが損害に直結する.趣味のモノづくりとは何もかも違う世界で,意識は大きく変わった.また,人的リソースと期間が限られているなかで,どうバランスを取るか,という点は常にトライ&エラーで工夫していた.
  • OJTトレーナーやレビュア経験.コミュニケーション面で,どうすれば相手に伝わるのかひたすら苦心した.技術面(?)では,何をレビューしなくても大丈夫かという勘所(手の抜きどころ)を得たような気がする.
  • 会社はそう簡単には変わらないという感触.会社の技術推進部門に異動させてもらって,「さて,会社全体で技術重視の方向に舵を切ってもらいますか」とイキったが特に何も出来なかった(一番悲しいのはその障壁が部門内にあったことだった).

何を得なかったか

一方,得たいと思っていたものはあまり得られなかったように思う.

  • 技術力.趣味で培った以上のことを何も得なかった.配属直後から「チームで一番技術に詳しいのはお前だから」と言われていた.
  • 顧客が本当に求めるもの.本当に顧客が欲しているものを作っている,という感触は得られなかった.これ以上の言及は控えたい.

なぜ転職しようとしたか

「得なかったもの」の裏返しである.技術力を活かして,顧客が本当に求めるものを作りたかった.

より根底には,3つの事柄が関係している気がする.

  • 小学生の頃から続く「プログラミングって楽しい」とか「プログラミングなら何でも出来る」という感覚
  • 作ったものを誰かに使ってもらったときの「嬉しい」という感情
  • 「やりたいこと」「できること」「求められること」が合致するとベターだという大学の講義で聴いた話

ずっと転職を考えていたが,最後のひと押しは同僚の転職だった.

なぜ転職まで6年強在籍していたか

2点ある.

  • 限界を見たかった.自分が現場や会社をどれだけ変えられるか,一通り試しておきたかった.
  • 漠然とした不安.Web系はSIerよりハードワークなのだろうという偏見と,SIerから転職出来るだろうかという不安があった.

限界を見たという納得感と,SIerに残り続けることの不安にかられ,転職に至った.

なぜ今の会社に決めたか

先に非礼を詫びておく.

情熱プログラマに「一番の下手くそでいよう」という節があるが,これを最も実現できそうだったから,というのが理由である.

言い訳がましいが,このフレーズに甘えるつもりはなく,相応の成果を出すようには努めている.

転職活動の思い出

転職活動は大変だったが楽しかった.面接の技術的な質問は腕試し感覚だったし,何より「サービスを作ってる人が目の前にいる」のがとても眩しかった.

印象的な問答はよく覚えている.

私「今の会社だと,そもそも『理想の開発環境』にこぎつけるまで10年単位で掛かる.待っていられない」
面接官「その『理想の開発環境』というのは具体的には」
私「CIが回っているとかそのレベル」
面接官「えっ(笑)」
私「えっ(笑)」

SIerから自社サービス,で異世界転生モノが書けるのではないだろうか.私は書かないが.

今の会社はどうか

自分のスタイルとの合致度は大きく上がったと思っている.

会社固有のものか,自社サービス系なら当たり前のものか,という区別はついていない.

合っているところ

  • 自由.「成果を出す」という一点が満たせれば,いつ来ていつ帰ってもよいし,技術選定に制限はないし,周辺機器や書籍も手に入る.
  • 技術スタックの分散が大きい.新しい技術に触れやすいし,どの技術も誰かが詳しいので困ったときもなんとかなる.
  • コミュニケーションがいい意味で雑.相互のリスペクトが前提となるが,オブラートや遠慮みたいなところに脳のリソースを割かなくて良い.これはエンジニアに限った話ではない.
  • チームリーダでもコードが書けるSIerだとリーダはリーダ業に徹していたが,現職はメンバの自走力(?)が高くて,リーダリーダする必要はない(そっと背中を押す感覚でいる).メンバと同じく作業していることが多い.

合っていないところ

  • 申請方法どこに載ってんだみたいなことが多い.相手の時間を奪いたくなくて聞くのは避けたい人間なのだけど,結局初回は聞くことになりがち.個人的にちょっと相性が悪い.

そのほかの感想

  • みんなすごい(こなみかん).インターンの学生がめちゃくちゃ成果出したりもする.正直劣等感に苛まれることもあるが,隣の芝生は何とやら,自分は自分なりに成果を出せるポイントを見つけてやっていくしかないのではと感じている.

今何をやっていて,今後どうしたいか

今は,後述する「やりたいこと」に掲げたことをやっている.

今後については,「5年後にXX」といった明確な目標はまったくない.これには,大学生の頃に聴いた 計画的偶発性理論 (Planned Happenstance Theory) に従っている,という背景がある.

ただし,やりたいこととやりたくないことは持つようにしている.内容は変化するとしても,やりたいことが出来て,やりたくないことを極力やらなくていい,そんな場所を今後も漂っていくのだと思う.

やりたいこと

  • コーディング.ソフトウェアアーキテクチャの設計等も含む.何らかの形で「自分が書いたコード」がプロダクトに入っていてほしいと常に思っている.
  • プロダクトの成長.「より多くのユーザにより良いプロダクトを届ける」ことで満たされた気持ちになるような気がしている.これはここ半年で強く思うようになった.
  • プライベートでのサービス開発.既に14年半続けているが,アイデンティティとして昇華させたい.

やりたくないこと

  • マネジメント中心の仕事.人をどうこうする,というのは最も苦手な部分であるし,精神的に疲弊するため.
  • 兼務マルチタスクは疲弊する.

おことわり

これはポエム.一個人の意見に過ぎない.

変数名などの命名のお供に.Webサービス "Zenrei" を公開しました

Webサービス "Zenrei" を公開しました. GitHubのスター数上位のリポジトリから,クラス名・メソッド名・変数名の使用実績を検索できるサービスです.悩ましい命名のお供にご活用ください.

機能

  • 使用実績GitHubスター数上位リポジトリから,入力したクラス名/メソッド名/変数名の使用頻度・使用箇所を表示
  • 類語 … "calculate" - "estimate" のように,各単語の類語を表示
  • betaにつきRubyのみ対応

API

活用例

"calculate" にするか "calc" にするか

f:id:no_clock:20190302164834p:plain

"data" が複数形なので、 "datas" は使われていない

f:id:no_clock:20190302165148p:plain

ソースコード

GitHubに公開しています.あまり綺麗ではないです.

技術要素

言語・フレームワーク

  • フロントエンド: Vue.js + Bootstrap
  • サーバサイド: Go (Echo framework)
  • データベース: MongoDB, SQLite3(日本語WordNetのみ)
  • その他: Docker, Ruby(データ解析)

Vue.jsとGoは初でしたが,意外とすんなり出来ました.

クラス名/メソッド名/変数名の解析

ripperライブラリを使用しました.Ruby標準ライブラリで,ソースコードを与えるとS式にして返却してくれたりします.

類語の検索

日本語 WordNetを使用しました.SQLiteデータベースとして配布されており,synset(概念)というグループ化によって,類似する単語を探し当てることが出来ます.

なお,複数形・過去形などは原形(lemma)に戻して類語を調べています.これにはgolemライブラリを使用しました(GitHub: aaaton/golem).当初はwink-lemmatizerを用いてフロントエンド側で対応する予定でしたが,辞書サイズが大きくjsファイルが肥大化するためサーバサイドで対処しました.

細かいUI/UX

  • APIリクエストには一定の遅延時間を設定し,リクエスト数が過大とならないようにしました(lodashで実現).
  • APIリクエスト中はスピナーを表示させ,処理中であることを可視化しました.
  • レスポンシブデザインで環境が違っても大きく崩れないようにしました.

PKCS #1(RFC8017)のRSA鍵フォーマット 日本語訳

PKCS #1 / PKCS #8に準拠した,.NET向けRSA暗号鍵エンコーダ・デコーダライブラリをNuGetに公開しました.ソースコードや使い方はGitHubにあります.

この記事では,PKCS #1での鍵フォーマットについて説明します.

PKCS #1 (RFC8017)

RFC 8017 - PKCS #1: RSA Cryptography Specifications Version 2.2

公開鍵暗号標準であるPKCSのドキュメントのひとつ PKCS #1(RFC8017) は,RSA暗号の仕様を定めたものです.暗号化や署名のアルゴリズムはもちろんですが,鍵の表現方法についてもAppendixに記載があります.

鍵の表現方法を定めた部分を訳してみます.

RFC8017 Appendix A. ASN.1 Syntax

付録 A. ASN.1 構文

A.1. RSA鍵表現

  この章では,RSA公開鍵・秘密鍵のASN.1オブジェクト識別子を定義するとともに, RSAPublicKey と RSAPrivateKey の型を定義します.  
  定義の対象用途には,X.509証明書,PKCS #8(RFC5958),PKCS #12(RFC7292)を含みます.

  オブジェクト識別子 rsaEncryption は, A.1.1 と A.1.2 で定義されるRSA公開鍵・秘密鍵を識別するものです.このオブジェクト識別子(OID)に関連付けられているAlgorithmIdentifier型のパラメタフィールドは,NULL型の値を持つものとします(SHALL).

      rsaEncryption    OBJECT IDENTIFIER ::= { pkcs-1 1 }

  この定義は,multi-prime RSAをサポートするよう拡張されていますが,過去のバージョンと下位互換性があります.

A.1.1. RSA公開鍵構文

  RSA公開鍵は,ASN.1のRSAPublicKey型により表現されます:

      RSAPublicKey ::= SEQUENCE {
          modulus           INTEGER,  -- n
          publicExponent    INTEGER   -- e
      }

  各フィールドは次のような意味を持ちます:

  - modulus: RSA の modulus n
  - publicExponent: RSA の public exponent e

A.1.2. RSA秘密鍵構文

  RSA秘密鍵は,ASN.1のRSAPrivateKey型により表現されます:

      RSAPrivateKey ::= SEQUENCE {
          version           Version,
          modulus           INTEGER,  -- n
          publicExponent    INTEGER,  -- e
          privateExponent   INTEGER,  -- d
          prime1            INTEGER,  -- p
          prime2            INTEGER,  -- q
          exponent1         INTEGER,  -- d mod (p-1)
          exponent2         INTEGER,  -- d mod (q-1)
          coefficient       INTEGER,  -- (inverse of q) mod p
          otherPrimeInfos   OtherPrimeInfos OPTIONAL
      }

  各フィールドは次のような意味を持ちます:

  - version: 将来の改訂を考慮するためのバージョン番号.0とします(SHALL).ただし,multi-primeを使用する場合は1とします(SHALL).
            Version ::= INTEGER { two-prime(0), multi(1) }
               (CONSTRAINED BY
               {-- otherPrimesInfosを含む場合,versionは必ず multi(1) とすべきです --})
  
  - modulus: RSA の modulus n
  - publicExponent: RSA の public exponent e
  - privateExponent: RSA の private exponent d
  - prime1: n の素因数 p
  - prime2: n の素因数 q
  - exponent1: d mod (p - 1)
  - exponent2: d mod (q - 1)
  - coefficient: CRT(中国剰余定理) の coefficient q^(-1) mod p
  - otherPrimeInfos: 追加の素数を r_3, ..., r_u, の順に含みます.version 0の場合は省略する(SHALL).version 1の場合は1つ以上のOtherPrimeInfoを含みます.
            OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo

            OtherPrimeInfo ::= SEQUENCE {
                prime             INTEGER,  -- ri
                exponent          INTEGER,  -- di
                coefficient       INTEGER   -- ti
            }
  
    OtherPrimeInfoのフィールドは次のような意味を持ちます:
      - prime: n の素因数 r_i (i >= 3)
      - exponent: d_i = d mod (r_i - 1)
      - coefficient: CRT(中国剰余定理)の coefficient t_i = (r_1 * r_2 * ... * r_(i-1))^(-1) mod r_i

  注意: RSA秘密鍵は,流出や改ざんからの保護が重要です.保護方法についてはこの文書の範囲外です.秘密鍵やその他の暗号データを保存・配布する方法については,PKCS #12やPKCS #15で記述しています.

読み解くポイント: RSA, ASN.1, CRT

定義は明確ですが,いくつか前提知識を必要とします.

PKCS #1の鍵表現の使われどころ

-----BEGIN RSA PUBLIC KEY-----で始まる文字列を見たことはあるでしょうか? あれは,PKCS #1形式によってRSA公開鍵が表現されています.

(※BEGIN PUBLIC KEYで始まるのはPKCS #8形式です.そちらはまた別途.)

$ ssh-keygen -f .ssh/id_rsa.pub -e -m pem
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAtH53M23IIXvXnM6d4nrZ9Q3BCU/HSMoYnZHlU6ytPXWZ5Y9oGAdX
4S2cMNOt7ZbiXXT9aRFyQwv80MCd6yMfrkHgFG0V2Oim59R1zypVXm7pvQmNApx3
Qvp+YMeEyLveFvn+wdwkeUSW1ag9D4ZIRwIE3kTqVxEj5BXElaaTW0HxYRDWVbrA
MU9RSekhTq2bHRSKIfMcbqtu/tFj9SS/46XiCPd05wolorAYTY3y2ntPVc9hbepf
IbxoiADm75pjxM1wg8ESOOmxSTQJO4hmdOEFRkMfRFFB65o2anN89Ubd1WJARwP5
+MYnliqPN3PO7eSqBw7qEzj+wqmRoX2YQwIDAQAB
-----END RSA PUBLIC KEY-----

この公開鍵は,PKCS #1で定める RSAPublicKey を,DERとBASE64で符号化したものです.

これを,Asn1PKCSでデコードしてみましょう.

var key = "MIIBCgKCAQEAtH53M23IIXvXnM6d4nrZ9Q3BCU/HSMoYnZHlU6ytPXWZ5Y9oGAdX4S2cMNOt7ZbiXXT9aRFyQwv80MCd6yMfrkHgFG0V2Oim59R1zypVXm7pvQmNApx3Qvp+YMeEyLveFvn+wdwkeUSW1ag9D4ZIRwIE3kTqVxEj5BXElaaTW0HxYRDWVbrAMU9RSekhTq2bHRSKIfMcbqtu/tFj9SS/46XiCPd05wolorAYTY3y2ntPVc9hbepfIbxoiADm75pjxM1wg8ESOOmxSTQJO4hmdOEFRkMfRFFB65o2anN89Ubd1WJARwP5+MYnliqPN3PO7eSqBw7qEzj+wqmRoX2YQwIDAQAB";
var rsaParams = PKCS1DERDecoder.DecodePublicKey(key);

Console.WriteLine("n(modulus)       : 0x" + BitConverter.ToString(rsaParams.Modulus).Replace("-", string.Empty));
Console.WriteLine("e(publicExponent): 0x" + BitConverter.ToString(rsaParams.Exponent).Replace("-", string.Empty));
n(modulus)       : 0xB47E77336DC8217BD79CCE9DE27AD9F50DC1094FC748CA189D91E553ACAD3D7599E58F68180757E12D9C30D3ADED96E25D74FD691172430BFCD0C09DEB231FAE41E0146D15D8E8A6E7D475CF2A555E6EE9BD098D029C7742FA7E60C784C8BBDE16F9FEC1DC24794496D5A83D0F8648470204DE44EA571123E415C495A6935B41F16110D655BAC0314F5149E9214EAD9B1D148A21F31C6EAB6EFED163F524BFE3A5E208F774E70A25A2B0184D8DF2DA7B4F55CF616DEA5F21BC688800E6EF9A63C4CD7083C11238E9B14934093B886674E10546431F445141EB9A366A737CF546DDD562404703F9F8C627962A8F3773CEEDE4AA070EEA1338FEC2A991A17D9843
e(publicExponent): 0x010001

できました.

Raspberry PiでNHKラジオ第一の緊急地震速報を検出する

P2P地震情報では,NHKラジオ第一の放送から緊急地震速報の発表を検出しています. 今回,その検出をPCではなくRaspberry Piで行うように変更しました.そのときの記録です.

前提: 自動音声による放送

NHKラジオ第一では,緊急地震速報(警報)が発表されると,通常の放送を中断して緊急地震速報の放送が入ります.

放送の内容は下記の通り.事前に収録した音声で自動的に放送されているようです.

(チャイム音)
緊急地震速報です.(震源名)で地震.次の地域は,強い揺れに警戒してください.(地域名)

緊急地震速報です(以下繰り返しの後,該当地域の方々への注意喚起など)

「発表されたこと」だけを検出する場合,冒頭の音声をパターンマッチするだけで実現が出来ます.これをやります.

材料

Raspberry Piによる録音

アダプタを接続するだけです.

$ lsusb
Bus 001 Device 002: ID 0d8c:013c C-Media Electronics, Inc. CM108 Audio Controller
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

$ arecord -l
**** ハードウェアデバイス CAPTURE のリスト ****
カード 1: Device [USB PnP Sound Device], デバイス 0: USB Audio [USB Audio]
  サブデバイス: 1/1
  サブデバイス #0: subdevice #0

$ arecord -L
(省略)
plughw:CARD=Device,DEV=0
    USB PnP Sound Device, USB Audio
    Hardware device with all software conversions

$ arecord -D plughw:1,0 test.wav
録音中 WAVE 'test.wav' : Unsigned 8 bit, レート 8000 Hz, モノラル

Python3による音声のパターンマッチ

GitHubにソース公開しています.見せるように作っていないのでかなり汚いです.

github.com

技術的なポイントはこのへん.

  • 音声波形そのままではなく,FFT変換した周波数スペクトルでパターンマッチング
  • FFT変換はNumPyライブラリで実施(お手軽)
  • 精度(しきい値)は人力で調整.といっても大した手間ではない

緊急地震速報の検出のためのポイントはこのへん.

  • 検出負荷を下げるために正解データは短く
    • チャイム音は2回繰り返すうち1回分
    • 自動音声は冒頭の「緊急地」のみ
  • 必ずチャイム音と音声をセットで検出(過去,チャイム音を用いた啓蒙放送等があったため)

※正解データは著作権の関係で公開できません.

実行

arecordの標準出力をPythonスクリプトの標準入力に食わせます.

$ arecord -D plughw:1,0 -t raw | python3 fft_analyze.py

f:id:no_clock:20190203233109p:plain

過去の放送音声を流してみたところ,正しく認識されました.CPU使用率は約20%,良い感じです.

(失敗)pyaudioで録音&パターンマッチ

そもそもpyaudioライブラリを使えば,Pythonで完結させることが出来ます.しかし,失敗しました.

f:id:no_clock:20190203235500p:plain

Input overflowedの表示の通り,録音データをリアルタイムに処理出来ていません.

arecordでは8000Hz, 1ch, 8bitという低いデータレートで録音していましたが,これはarecord内部でリサンプリングされたものでした.一方,pyaudioにはそうした機能はありません.デバイスが対応する44100Hz, 1ch, 16bitという高いデータレートで録音するしかなく,リアルタイムに扱うには厳しい量でした.

その結果,arecordの標準出力から食わせる,という方法に落ち着きました.

参考

P2P地震情報14周年: 個人サービスを長く続けるための技術

個人開発 #2 Advent Calendar 2018 8日目の記事です.

せっかく作った個人サービスを長く続けていくための技術についてお話ししようと思います.

3行でまとめると

  • Dogfooding … 使い続ける
  • Maintenance-free … とにかく省力化する
  • Write Code Every Day … 小さい変更を積み重ねる

P2P地震情報

P2P地震情報というサービスを開発・運用しています.

気象庁地震情報やユーザ同士の「揺れた!」という地震感知情報を,P2Pネットワークによって共有するサービスです.

現在のサービス構成は下記の通りです.本体はP2Pですが,サーバの役割は多岐にわたります.なお,耐災害性の観点から,一部機能は有志の方々のご協力により全国数ヶ所に分散しています.

祝14周年🎉 (なお理想と現実は異なる)

2004年12月に公開し,今年で丸14年です.思った以上に長く続いています.

残念ながら,やりたいことは十分に実現出来ていません.GUIアプリケーションは未だにVB6製で,なかなかのレガシーです.*1

それでも,14年間続けることができました.なんだかんだで良かったと思っています.どうして続けられたのかというと,これから話す3つの技術が大事なのではと感じています.

サービスを続けていくための技術

Dogfooding: 使い続ける

サービスを続けるには,モチベーションを維持し続けることが重要です.

その最も簡単な方法が,自分自身がモチベーションの源泉となるように,サービスを自ら使い続けることだと感じています.

昔,LaTeXのオンラインコンパイラやPDFファイルの画像化といったサービスを公開していましたが,自分が使わなくなった途端にメンテナンスしなくなり,外部からアクセスは続いていたものの公開を終了したことがあります.

Maintenance-free: とにかく省力化する

モチベーション(と時間)を無駄にしないために,維持するためにかかるコストを最小限に抑えます.

色々ありますが,一番やってよかったのはひとりSlackでした.

サービスに関わるあらゆるメール,通知,実行ログをSlackに集約し,「とりあえずSlackの新着を見ればよい」という状態に辿り着きました.メールの未読消化と比べて圧倒的な省力化です.

f:id:no_clock:20181206231200p:plain

f:id:no_clock:20181206231214p:plain

Write Code Every Day: 小さい変更を積み重ねる

学生から社会人になり,「まとまった時間が取れない」「しかし週末に一気にやる気持ちにもなれない」という矛盾する悩みを抱えていました.数年間悩み続けた末,John Resigの記事に出会いました.

John Resig - Write Code Every Day (日本語訳参考: 毎日コードを書くこと - snowlongの日記

学生時代の開発スタイルではいけないと気付かされました.

それから,100日以上毎日コードを書き続けました.1~2行しか書かない日もありましたが,それでも効果は絶大でした.

f:id:no_clock:20181206231231p:plain

C#版のGUIアプリケーションも徐々に実装が進んでいて,一部機能は既に動作します.レガシーをそろそろ捨てられる日が来そうです.

f:id:no_clock:20181206231243p:plain

楽しい個人開発ライフを!

世の中の便利な個人サービスが,少しでも長く続きますように…!

明日は @ktrkmk さんです.お楽しみに!

*1:余談ですが,Windows 10でもVisual Basic 6.0は動作しますし,IDEコンパイルもできます