Windows アプリ開発で利用している Web サービス

近年は GitHub を始めとした Version Control Systems (VCS) のホスティングサービスと連携する形で、プログラミングやソフトウェア開発に関する様々な Web サービスが提供されています。私は専ら Windows のデスクトップ・アプリケーションを開発していますが、この領域においても便利な Web サービスが数多く登場してきました。そこで、この記事では、私が Windows アプリを開発する際に利用している Web サービス (AppVeyor, Codecov, Codacy) について纏めていこうと思います。

AppVeyor

AppVeyorWindows サーバ上で提供される継続的インテグレーション (CI: Continuous Integration) の Web サービスです。CI サービスとしては Travis CICircle CI などが有名ですが、AppVeyor は Windows .NET Framework、Visual Studio などのサポートが充実している事もあり、Windows アプリやサービスの開発者を中心に利用者を増やしているようです。

私が実際に CI と言うものに触れたのは AppVeyor が初めてでしたが、利用して感じた利点は主に以下の 2 つです。

  • ビルド結果のスナップショットが自動的に保存されていく
  • ローカル開発環境とは異なる環境でビルドおよびテストを実行する事ができる

VCS と言う概念が普及した事もあり、ソースコードに関しては私も長らく Git を用いてバージョン管理を行っています。しかし、最終的なビルド結果に関しては、残念ながら管理がおざなりになる傾向にありました。そのため、何らかの理由で以前のバージョン結果が必要になった時、特定のタグやコミットを基にして再度ビルドしなければならない事も多く、複数のリポジトリが関係する場合には必要な状態に戻しきれているのか不安な事もありました。AppVeyor は、ビルド結果をダウンロード可能な形で残す事ができるため、これらの手戻り作業が発生せず、必要な時点のスナップショットを確実に取得できるのは大きな利点だと感じます。

また、私の開発環境は、2 台のデスクトップ PC と 1 台のノート PC と言う 3 台構成となっていますが、これらの環境は概ね同じ構成となっているのでローカル環境では気付かなかった不都合が発生する事があります。この問題を幾らか改善する方法の一つとして、AppVeyor 上での実行が役に立っていると実感するようになりました。例えば、(副次的ではありますが)言語設定やタイムゾーンがローカル環境とは異なる設定となっている都合上、その辺りに起因するケアレスミスを手軽に事前確認する手段としても機能しています。

依存関係の解決

ここからは、AppVeyor の設定ファイルを記述する際に躓いた事項について、いくつか記載していきます。尚、これらのほとんどは CubeICE の AppVeyor.yml で問題となった事なので、リンク先の設定ファイルが具体例として良いかもしれません。

まず最初の課題は、ビルド時の依存関係の解決でした。AppVeyor 上でビルドする時、ビルド対象となるプロジェクトが NuGet で公開されているパッケージのみに依存しているのであれば楽なのですが、そうではない場合には少し工夫が必要となります。私が直面した問題は、以下の 2 点でした。

  • プライベートまたは公開前の NuGet パッケージに依存する場合
  • VC++ で記述されているような非マネージ・ライブラリに依存する場合

CubeICECubeRSS Reader が依存している自作ライブラリは、最近まで NuGet 上には公開せずプライベートな状態が続いていました。*1 また、現在においても、これらのプロジェクトの master ブランチは公開前のバージョンを参照している事があるため、何も対策せずにビルドするとエラーが発生します。

この問題に対しては、依存する自作ライブラリも含めて AppVeyor 上で管理する事で解決を図りました。AppVeyor は毎回 NuGet パッケージを生成し、そのパッケージには静的な URL でアクセス可能です。そこで、下記のように NuGet のソースとしてそれらを追加する事で、プライベートな NuGet パッケージを参照する事ができるようになります。

- nuget sources add -name Cube.Core -source https://ci.appveyor.com/nuget/cube.core
- nuget sources add -name Cube.FileSystem -source https://ci.appveyor.com/nuget/cube.filesystem
- nuget sources add -name Cube.Images -source https://ci.appveyor.com/nuget/cube.images
- nuget sources add -name Cube.Forms -source https://ci.appveyor.com/nuget/cube.forms
- nuget restore Cube.FileSystem.SevenZip.Ice.sln

次に非マネージ・ライブラリに依存している場合です。これは、自力でダウンロードして出力ディレクトリにコピーする方法で解決を図りました。例えば、CubeICE は 7z.dll と言う 7-Zip のライブラリ (正確には カスタマイズ版) に依存していますが、このライブラリをダウンロードして展開する設定は下記のようになります。

- ps: Start-FileDownload https://ci.appveyor.com/api/projects/clown/7z/artifacts/7z-x64.zip?job=Platform:+x64
- 7z x -o"Applications\Ice\Progress\bin\Release" 7z-x64.zip

尚、カスタマイズ版の 7-Zip ライブラリも AppVeyor で管理している都合上、上記ではその生成結果をダウンロードしています。もし AppVeyor 上で完結できない場合であっても、同様の方法で必要なライブラリをダウンロードして展開する事で、多くのケースは解決できるのではないかと思います。

最終成果物 (Artifacts) の整形

AppVeyor はビルド結果などの最終成果物を Artifacts と呼んでおり、これらは特定の URL からダウンロード可能となります。この Artifacts に関する設定のうち、NuGet パッケージについては "publish_nuget: true" と言う記述だけで自動的に生成してくれますが、それ以外のものについては "artifacts" の項目に記述する必要があります。この項目は、単に生成する事だけを考えるのであれば "path" にビルドの出力ディレクトリを記述すれば OK です。しかし、個人的にはダウンロード用の URL が少し不格好になるのが気になり、何とかならないかとドキュメント等で解決方法を模索していました。

Artifacts の整形

この問題に対しては、最終的には "after_build" の項目で好みの形になるように整形する方法を採用しました。例えば、CubeRSS Reader のメインプログラムを CubeRssReader.zip と言う形で Artifacts に保存する場合、以下のように記述します。

after_build:
  - xcopy /Y /I Applications\Rss\Reader\bin\Release CubeRssReader
artifacts:
  - path: CubeRssReader

尚、場合によっては "x86" や "x64" のように、設定によって動的に変更される値が必要になりますが、AppVeyor において利用可能なこれらの値は Environment variables に記載されています。例えば、"x64" と言う値が必要な場合は %PLATFORM% と記述します。

複数ブランチを個別に CI 管理

全てのブランチを単一の AppVeyor プロジェクトで管理するのであれば良いのですが、場合によってはブランチ毎に個別に管理したい時もあります。例えば、CubeRSS Reader では WebView に Chrome ブラウザを利用したもの (chrome ブランチ) を実験的に公開していますが、これを AppVeyor 上でも master ブランチとは区別して管理したいと思うようになりました。

複数ブランチを個別に CI 管理

この課題には、AppVeyor 上で GitHub(または類似の VCS ホスティングサービス)の同じリポジトリを参照するプロジェクトを複数個作成し、それぞれの設定ファイルの "branches" で対象とするブランチを絞る事で対応するようです。しかし、この場合でも意図した動作になるように設定するのは意外と難しく、最終的には以下のような形になりました。

  1. master ブランチの設定ファイル (AppVeyor.yml) とは別に、chrome ブランチ専用の設定ファイルを作成する (AppVeyor.Chrome.yml)。そして、AppVeyor.yml の "branches" には master、AppVeyor.Chrome.yml には chrome ブランチのみを対象とするように記述する。
  2. AppVeyor の各プロジェクトの Settings で、"Custom configuration .yml file name" の項目に対応するブランチの設定ファイル名をそれぞれ入力する。
  3. chrome ブランチ用プロジェクトの Settings で、"Branches to build" の項目を "On branches specified below" に選択し、その下に chrome と入力する。

問題となったのは 3. に記載した部分です。AppVeyor は GitHub に push したタイミングで、指定された設定ファイルに従って実行するかどうかを決定します。この時、master ブランチには AppVeyor.Chrome.yml が存在しないため、chrome ブランチ用のプロジェクトでは初期設定が利用されます。初期設定では全てのブランチで CI を実行する事になっているので、意図しないブランチの CI 実行結果も反映される事となりました。この誤動作を防止するために、プロジェクトの Settings でも chrome ブランチ以外の実行を防止するように設定しています。

Codecov

Codecov

Codecov は、GitHub などにホスティングされているリポジトリのテストカバレッジを可視化するための Web サービスです。Codecov 自体は、何らかのユニットテストが実行された結果を表すファイルを解析して表示するものであるため、多くの場合 CI サービスと連携して利用されます。Windows アプリ開発の場合は前述した AppVeyor 上でユニットテストを実行し、その結果をファイルに出力して Codecov に送信します。

Codecov を利用するようになって最も良いと感じているのは、プログラムを 行単位で テストが実行されたかどうかを確認する事ができると言う点です。例えば、引数を伴うメソッドを実装する場合、最初に引数の null チェックを記述しがちですが、Codecov 上で確認すると、該当の行が黄色(C1 カバレッジが未達成)のままな事があります。

この時、まず最初に検討するのはテスト不足ですが、同時に「この null チェックは不要なのではないか?」と言う可能性についても検討します。そして、呼び出し元なども確認した結果、該当のチェックが不要であると確認できた場合には、単純に消去するか、もしくは assert 文に置換します。このように、自分のプログラムを行単位でテストが実行されているかどうかを定期的に確認すると言う行為が、セルフ・コードレビューのような機能も果たす事が分かりました。

カバレッジの数値との付き合い方

カバレッジの数値、例えば 80% や 100% のような値をどう捉えるかに関しては、当初よりもかなりポジティブな印象を抱いています。例えば、「enum に対応する文字列を取得する」ような単純なメソッドのテストコードに対しては、長らく「このテストに、カバレッジの数値を上げる以外の意味があるのだろうか?」と言う疑問を抱いていました。しかし、これらのテストコードによって、リファクタリング時の Typo による思わぬバグを早期に発見できた言う経験が何度もあり、一見すると無駄に見えるテストの重要性を実感するようになりました。

カバレッジの数値を上げるためだけの近視眼的なテストコードを記述する事によって、それよりも重要なテストコードを見逃すのではないかと言う不安は依然としてあります。ただ最近は、ある程度は仕方がないと割り切り、以下のような指針で実装およびテストコードの記述を行っています。

  1. 初期リリースまでは、取り合えずカバレッジの数値(80%~90% 程度)を指標としてテストコードを記述する。この結果、初期リリース時点では、正常ケースおよび容易に予想可能な異常ケースのテストに留まる。
  2. リリース後に何らかの不都合が発覚した場合、必ず最初に該当の不都合が再現するテストコードを記述する。そして、テストを実行してレッド・シグナルを確認する。
  3. 該当部分の実装コードを修正し、テストを実行してグリーン・シグナルを確認する。

このサイクルによって、完全ではないにしても「少しずつだが、良くなってはいる」事を実感できるのは、テストコードを含めた保守を続けていく、あるいは習慣化する上でプラスに働いていると思います。

AppVeyor との連携設定

前述したように、Codecov は何らかの CI サービスと連携して利用する事がほとんどです。ここでは、AppVeyor との連携方法について記載します。AppVeyor では、まず始めに "test_script" の項目でテストを実行します。私の場合、ユニットテストフレームワークNUnitカバレッジ計測ツールは OpenCover を利用しており、具体的な記述例は下記のようになります。

test_script:
  - >
    packages\OpenCover.4.6.519\tools\OpenCover.Console.exe
    -register:user
    -target:nunit3-console.exe
    -targetargs:Cube.FileSystem.SevenZip.App.Ice.Tests.dll
    -targetdir:Applications\Ice\Tests\bin\Release
    -returntargetcode
    -hideskipped:All
    -output:CoverResult.xml
    -filter:"+[*]* -[*]*NativeMethods -[*]*Properties.* -[*]*.Program"

尚、OpenCover の実行プログラムは NuGet 経由で取得するため、テスト対象となるプロジェクトの packages.config に OpenCover を含める必要があります。また、複数のテストを実行する場合 -mergeoutput オプションを記述する事で結果を 1 つのファイルに纏める事ができます。

OpenCover の実行で躓いた点は -returntargetcode オプションでした。このオプションがない場合、テストが失敗した場合であっても正常終了したと見なされるようになります。その結果、不完全な結果が Codecov に送信されてカバレッジの推移が見づらくなると言う問題が発生しました。これに対して -returntargetcode オプションを指定した場合、テストに失敗したタイミングで AppVeyor が実行を中止します。

after_test:
  - "SET PATH=C:\\Python34;C:\\Python34\\Scripts;%PATH%"
  - pip install codecov
  - codecov -f CoverResult.xml

テストの終了結果を Codecov に送信する設定は上記のようになります。Codecov は、プロジェクト毎にアップロード用 API key を割り当てるのですが、AppVeyor 経由だと API key は必要ないようです(ローカル環境からアップロードしようとした場合は必要)。

Codacy

Codacy

Codacy は、コードレビューの自動化 Web サービスです。GitHub に push したタイミングで対象となるリポジトリのプログラムに対して何らかの静的解析が実行され、その結果を Web 上で閲覧する事ができます。Supported Languages によると、対応しているものは ScalaJavaJavaScriptPythonRubyPHP を始めとした 14 種類で、この中に C# も含まれています。また、Codacy はカバレッジのレポート機能も有していますが、現状では残念ながら C# のプロジェクトには適用できませんでした。将来的には、Codecov で行っている事も Codacy に統合できるかもしれません。

私が Codacy の存在を知ったのは数ヶ月前の事で、まだそこまで使いこなせている訳ではないのですが、なかなか良い感触を得たため、自分のプロジェクトに対して順次適用しています。Codecov の項目でも少しだけ触れましたが、ほとんど 1 人で開発を行っているとコードレビューを行う人がそもそも存在しないため、定期的なセルフ・コードレビューを意識する必要があります。Codacy は、この課題を補う方法の一つとして機能していると感じます。

Codacy は解析の結果、問題のある項目を Issue として登録します。そして、プログラマがその Issue を解決する方法としては、以下の 4 つが挙げられます。

  • プログラムを修正する
  • 該当の Issue を無視する (Ignore issue)
  • 登録された Issue を特定するのに使用されたルールを無視する (Remove pattern)
  • 該当ファイルの全ての Issue を無視する (Ignore file)

基本原則としては、プログラムを修正する事によって Issue の解決を図るべきとは思います。ただ、実際にやっていると様々な理由で、それだけでは行き詰まる事があるのも事実です。ここでは、私がこれまで「無視」を選択した Issue について、いくつか記録を残しておこうと思います。

Unused 系の Issue をどう扱うか

私が Codacy を数ヶ月利用した中で、最も判断に迷うのが Unused 系の Issue の扱いについてでした。Codacy は、未使用の変数やメソッドを発見した場合、それを Issue に追加します。これ自体は良い事で可能な限り修正(削除)すべきですが、問題になったのはその精度でした。

私も Issue が登録された場合、まずは自分の側に問題があると考えて解決方法を模索しますが、Unused 系に関しては、現時点の自分の知識ではどう考えても誤動作ではないかと思われる Issue が大量に登録されます。代表的なものは以下の 3 点です。

  • public な class の public なプロパティであり、該当のプロパティを使用しているテストコード等が存在するにも関わらず "Remove the unused private property" と言う Issue が登録されてしまう
  • 定義した次の行に変数を使用している記述があるにも関わらず "Remove this unused 'foo' local variable" と言う Issue が登録されてしまう
  • イベントを定義すると、同クラス内で Invoke する記述が存在し、該当イベントを利用しているテストコード等が存在するにも関わらず "Events should be invoked" と言う Issue が登録されてしまう

これらに関しては、今のところ「十分に検討したが、やはり誤動作だろう」と思ったものについては個別に Ignore issue を選択しています。C# の解析には Sonar C# が用いられているとの事なので、余裕があればそちらの方も少し見てみようかと思います。

その他に無視した Issue 一覧

上記以外で無視している Issue (Remove pattern) は以下の通りです。これらに関しては、音楽性の違いに依るものもあるので、無効にせず修正した方が良いと感じる人もいるかもしれません。

  • Empty "default" clauses should be removed
    "default: break;" と言う記述があると登録される Issue です。元々、switch 文の処理のない default 句は書かないようにしていたのですが、default 句は必ず記述する旨の Issue が登録されたので対応した所、今度は上記の Issue が登録されてしまいました。現状では、こちらの Issue を無視しています。
  • Control structures should use curly braces
    if 文や while 文などのブロック部分には必ず中括弧を記述すべきだと言う Issue です。個人的には、条件文とブロック部分の間に改行を挿入するスタイルは予期せぬバグを埋め込む可能性があり NG ですが、「1 行で書ききれるのであれば、中括弧は省略しても良い」と言う思想なので(if (condition) { return; } とは書きたくない)このIssue は無視しています。
  • Members should not be initialized to default values
    private object _member = null; のような記述はやめろと言う Issue です。null に関しては Issue に従って削除していますが、bool や int 等に関しては「その値をソースコード上で明示しておく事に意味がある」と感じる事も多いので、ケース・バイ・ケースで個別に無視しています。
  • Methods and properties that don't access instance data should be static
    あるクラスのメソッドが、そのクラスの状態にアクセスしないのであれば static にすべきだと言う Issue です。プログラムの見やすさを改善する意味で作成する private メソッドで指摘される事が多いようです。指摘内容は分からなくもないのですが、private メソッドはちょっとした修正で状態にアクセスする必要が出てくる事も多いので、この Issue は無視しています。

以上、私が現在の開発で利用している Web サービスの紹介でした。

*1:クラスやメソッドなどの公開インターフェースに対して破壊的な変更をまだまだ行っていたので、公開版とするのは時期尚早と考えていました。

広告を非表示にする

CubeRSS Reader 0.2.0β

CubeRSS Reader 0.2.0β をリリースしました。更新内容は リリースノート の通りですが、ここでは補足事項やその他の細かな事柄について記述します。

データフォルダの変更機能

今回のバージョンアップでは当初、データファイルの Load/Save 周りに関する改善のみを実施する予定でしたが、開発環境でテスト用データに切り替えるのが面倒になり、前倒しでデータフォルダの変更機能を実装する事にしました。この機能を利用する事で、間接的ではありますが、複数端末で同期すると言う需要を満たせるようになるのではと期待しています。

CubeRSS Reader では通常、AppData/Local/CubeSoft 下に CubeRssReader と言うデータフォルダを生成します。ユーザが、この初期設定を敢えて変更しようと考える場合、その多くは DropboxOneDrive のような同期型オンラインストレージの監視下にあるフォルダを指定する事が予想されます。この時、大きな問題となるのが「複数の端末が、同じデータフォルダに対して同時にアクセス(変更)する」と言う状況が起こり得る事でした。複数の端末から同時にアクセスされている状態で、同期しながら整合性を保つ事は当初より想定していませんでしたが、何かしら排他制御を行う事の必要性は感じていました。しかし、データフォルダ下に配置される各種ファイルの排他制御をどうするかについては考えがまとまっていなかったため、最初のリリースでは該当機能の導入を見送っていました。

0.2.0β では最終的に、データフォルダ直下に .lockfile と言うファイルを生成し、このファイルにアクセスしている端末の情報を保存すると言う方法を採用しました。CubeRSS Reader は、データフォルダへの初回アクセス時に .lockfile の存在を検知すると、以下の画像のように「読み取り専用モード」で起動します。

CubeRSS Reader 読み取り専用モード

尚、何らかの原因で CubeRSS Reader の終了処理が失敗し、.lockfile の情報が残ってしまう事が考えられます。他の端末で CubeRSS Reader を起動していないはずなのに読み取り専用モードとなる場合、データフォルダにある .lockfile を削除して CubeRSS Reader を再起動してみて下さい。また、同期型オンラインストレージの性質上、ある端末の .lockfile の内容が別の端末に反映されるまでには若干のタイムラグが存在します。そのため、現状では複数の端末で CubeRSS Reader を同時(あるいは非常に短い時間)に起動した場合の挙動は保証できませんので、その辺りは注意してご利用下さい。

データファイルの Load/Save 周りに関する改善

今回のバージョンアップで主眼としていたのは、データファイルの Load/Save 周りに関する改善でした。修正内容は大きく分けて 3 点です。

起動直後における Load 時間の改善

1 点目は、CubeRSS Reader 起動直後における Load 時間の改善となります。テスト環境では、大体 RSS フィードが 200 件位、新着記事(未読記事)が 4,000 件位の状態で動作確認をしていますが、メイン画面が表示された後、全項目が表示されるまでに結構なタイムラグが存在する事が当初からの懸念事項でした。

動作を解析した結果、この問題は(起動直後においては)不要なファイルが大量に読み込まれるために発生していたようです。該当部分を修正する事で、前述した条件下で、起動直後の Load 時間が 2 秒程度(0.1.1β)から 80 ミリ秒程度(0.2.0β)まで改善しました。

キャッシュファイルの扱いに関する修正

2 点目は、キャッシュファイルの扱いに関する修正です。RSS リーダは性質上、どれだけの RSS フィードが監視対象となるのかを予想できないため、全てのデータをメモリ上のみで処理すると問題になる事があります。そのため、CubeRSS Reader ではフィード毎にキャッシュファイルを生成し、タイミングに応じて Load/Save を実行する事で対応する予定でしたが、最初のリリースではいくつかのバグが残っており挙動が不安定でした。

0.2.0β では該当部分の修正を行い、確認した限りでは意図通りに動作するようになりました。尚、この修正に伴い、GUI 上ではまだ設定項目を用意していませんが、データフォルダにある Settings.json の Capacity と言う項目を変更する事でメモリ消費量をある程度管理できるようになりました。例えば、RSS フィードが 200 件位、未読記事が 4,000 件程度の条件で Capacity の値を変更した時のメモリ消費量の変化は下記のようになります(未読記事がゼロの RSS フィードを選択している場合)。

Capacity メモリ消費量
10 53.6MB
20 54.2MB
50 68.8MB
100 78.7MB
1,000 95.2MB

ただし、この結果はあくまで参考値です。メモリ消費量に関しては、RSS フィードの購読状況もそうですが、それ以上にメイン画面右側の WebView に表示される内容に非常に大きく依存します。そのため、現実問題としては、Capacity の値はそこまで気にする事はないかなと思います。

不要なキャッシュファイルの削除

最後は、不要なキャッシュファイルの削除機能に関する修正です。0.1.1β までのバージョンでは、特定のカテゴリに属する複数の RSS フィードをまとめて削除した場合、不要なキャッシュファイルが残ったままとなる不都合が確認されていました。この不都合は、前述した同期型オンラインストレージの監視下にあるフォルダをデータフォルダとして使用し始めると、ストレージ容量を必要以上に消費するなど大きな問題となり得るため、それまでに修正すべき内容として認識していました。0.2.0β では、該当部分の修正が完了し、正常に機能するようになりました。

0.2.0β で、各種データの管理機能に関わる基本的な部分に関しては、概ね修正が完了したかなと思います。今後は、キーボードショートカットを始めとした CubeRSS Reader の操作性の改善等に取り組んでいく予定です。

広告を非表示にする

CubeRSS Reader の開発動機などについて

CubeRSS Reader メイン画面

先日、RSS/Atom feed reader(RSS リーダ)である CubeRSS Reader の最初のバージョンを公開しました。この記事では、なぜ今さら RSS リーダを開発しようと思い至ったのか、その動機などについて記載します。

RSS リーダを開発した動機

RSS リーダと呼ばれる分野においては Google Reader(~2013 年)を始め、livedoor Reader(~2017 年*1)、AOL Reader(~2018 年)、Digg Reader(~2018 年)と国内外を問わず Web サービスが次々と提供を終了しています。現時点ではまだ Feedly など有名な Web サービスがいくつか残っていますが、これらを含めても今後どうなっていくかを考えた時に不安を覚えたため、自らが選択肢の一つになる事に挑戦したいと思ったのが CubeRSS Reader の開発動機となります。

当初、新たな RSS リーダを開発する上で、個人的に設定した方針は以下の 2 点でした。

  1. OSS として公開する
  2. ユーザ端末においてスタンドアローンで動作する

1 点目は良いとして、2 点目のスタンドアローンで動作する事に拘った理由ですが、前述したように RSS リーダと言う機能を提供する Web サービスは先行きが暗い状況となっています。実際、RSS/Atom フィードの仕組み自体は今後も様々な場面で利用され続けると思いますが、汎用的な RSS リーダ と言うもの自体に対する需要は低下傾向が続くだろうと予想されます。このような状況で新たに Web サービスを公開したとしても、そう遠くないうちに諸々の維持費などを考慮した上で、サービスの提供を継続するかどうかの判断を迫られる時がやってくる事は想像に難くありませんでした。したがって、少し言い方は悪いですが、下火となっている分野において長期間、継続的に選択肢の一つとして提供し続けるには、Web サービスではなく各ユーザ端末においてスタンドアローンで動作する形の方が可能性があると判断しました。

今後も開発が継続されそうと期待できるような体制の確立

CubeRSS Reader は今後も開発を続けていく予定ですが、RSS リーダの選択肢の一つとして認識してもらうには、少なくとも同じプログラマ位には「口先だけではなく、本当に開発が継続されそう」と言う期待感を持ってもらえるような何かが必要と感じていました。これに対する現時点での回答としては、ここ 1 年の AppVeyorCodecov などに対する習熟の成果と言う意味も込めて、継続的インテグレーション (CI) の整備、そして最低限のテストは用意されていると言う事を分かりやすい形で外部に見せると言うものになりました。

テストのないコードは悪いコードである。どれだけうまく書かれているかは関係ない。どれだけ美しいか、オブジェクト指向か、きちんとカプセル化されているかは関係ない。テストがあれば、検証しながらコードの動きを素早く変更することができる。テストがなければ、コードが良くなっているのか悪くなっているのかが本当にはわからない。

レガシーコード改善ガイド

良いコード、あるいは継続的な保守や機能拡張を実現できそうなコードとは何かと考えた時に、現代における回答の一つとして「テストのあるコード」が挙げられます。これについては、最近は自分に関連するプロジェクトでどの程度テストが完備されているかを分かりやすく示す手段として、Codecov の結果をバッジで表示する形を採用しています。カバレッジはテストが十分である事を必ずしも保証するものではないですが、分かりやすい指標が何もないよりは良いのではないかと言う感触を得ています。

尚、私の日頃の開発サイクルの詳細に関しては、Windows アプリ開発で利用している Web サービス (AppVeyor, Codecov, Codacy) を参照下さい。

Chrome ブラウザを利用する実験バージョン

ダウンロードページ にて配布している CubeRSS Reader 公式版では、Web ページ等の内容を表示するためのコンポーネント(内部ブラウザ)に .NET Framework 標準の WebBrowser を利用しています。このコンポーネントInternet Explorer (IE) のレンダリングエンジンで実現されていますが、2018 年現在では、最終版の IE11 であっても表示速度や Web ページの表示のされ方などに様々な問題を抱えています。

そこで CubeRSS Reader では、実験的な branch として Google Chrome、より具体的に言うと CefSharp と言うライブラリを利用するバージョンを並行して開発・公開しています。.NET Framework 標準の WebBrowser とのプログラム的な差異に関しては Behavior と呼ばれる機能を利用したクラス内でほぼ吸収できているため、比較的保守が容易な事もあり、当面はこの形を続けていく予定です。

尚、Chrome バージョンに関しては、現時点ではインストーラ等の正式な配布用ファイルは用意していません。AppVeyor 上でビルドする度にスナップショットが作成されますので、試してみたい方は x86 or x64 の Artifacts で表示されるリンクから Zip ファイルをダウンロードし、展開してご利用下さい。

Cube.Net.Chrome - AppVeyor

最初のリリースを終えての感想

私自身は、ここ 2, 3 年、C# をベースとして以下のような事を重点的に習熟してきました。

  • UI をブロックしないための非同期プログラミング(Task および async/await の利用)
  • Presentation Domain Separation (PDS) の実現

断片的な情報に依存している部分も多く、なかなか「これで良い」と確信できる境地に達する事ができずにいますが、今回、これまでに作成したものよりもやや複雑さを増した View を持つアプリケーションを概ね満足できるコード状態を保ったままリリースできた事には、それなりの達成感を抱いています。最初のリリースでは基本的な機能に留まっていますが、最初のスクリーンショットにあるように、100 件程度の RSS/Atom フィードと数千件の未読記事程度であればひとまずストレスを感じるような事もなく*2、出発点としてはまずまずかなと思います。

View の部分を除くと、クライアントのみで動作する RSS リーダで重要になるのは「登録されたフィードに対して、どのように定期巡回を実行するか」と言う点かと思います。これに関しては、基本的に RSS/Atom フィード(Web サイト)の追加は行うが削除はあまり行わないと言うユーザの性質と、追加された少なからぬ Web サイトがいずれ更新を停止してしまうと言う性質を鑑みて、現時点では更新頻度に応じて巡回頻度を変えると言う方法を採用しています。こうする事で愚直な定期巡回よりは、購読フィード数に対するスケーラビリティを確保できると期待していますが、実際どうなるかはもう少し様子を見る必要がありそうです。

今後の方針

今後しばらくは、基本的な動作が安定しているかどうかの確認と「RSS リーダとしての minimal set」がどこまでなのかを探りなら実装していく段階になるかと思います。最初のリリースに対する反響を見ている限りでは、やはり複数端末で同期して利用する事への需要は根強く、この点をどうするのかが課題になると感じています。スタンドアローンでも動作すると言う原則を崩さないためには、現時点では Dropbox などのストレージを同期するような Web サービスを利用する形がベストではないかと思っていますが、この辺りも含めていろいろ検討していく予定です。

CubeRSS Reader の更新履歴

*1:2014~2017 年は Live Dwango Reader として運営

*2:もちろん実行環境のスペックにも強く依存するとは思います。

広告を非表示にする