2023年5月5日金曜日

ソフトウェア開発における品質に対する態度の違いは顧客のニーズにあるのだろうと思った

 日本のソフトウェアを作る企業や企業内のソフトウェアを開発する部署に所属した経験から、品質に対する態度の違いみたいなものがあり、それがどうして生まれるのかについて思うところがあったので、記録しておく。

ここでの品質と言っているのはいわゆる品質保証でQAなどと呼ばれる部分だ。ソフトウェアの読みやすさとかそういった観点での品質もあるとは思うが、最終的な製品の品質の部分に対して考えてみる

開発手法自体は、大きく分けると2通りの開発手法があると思う。リーンソフトウェア開発とウォーターフォール開発だ。開発手法が違えば品質に対する考え方も取り組みも違ってくる。

ウォーターフォール開発の品質への取り組み

ウォーターフォール開発の場合は、計画重視の開発サイクルが特徴だと思う。プロジェクトの初期段階で厳密な要件定義と設計を行い、段階的に開発を進める。品質保証は、各フェーズの終了時に行われることが一般的であり、その過程で問題が見つかった場合は、適切な修正が行われる。
この各フェーズでの品質の保証というのが特徴的だと思う。製造業と同じように各工程で完璧なものを作れば、最終的に完璧なものができると言う考え方だ。
当たり前だが、完璧なものを作るのは無理なので、当然不具合がある。不具合があれば、工程を遡ってやり直せば良いのだが、そうはならない。
なぜ、そうはならないかというと、企業がなぜウォーターフォール開発を選択しているかに関係すると思う。ウォーターフォール開発を選択する企業はスケジュールを非常に重視する。理由はハードウェアのリリースだったり、ユーザー企業の都合だったりとさまざまだと思うが、スケジュールは死守されるものだ。そのためスケジュールが立てられるように見えるウォーターフォール開発を選択するのだと思う。これは、ソフトウェア開発を必要としている顧客からのニーズである。

リーンソフトウェア開発の品質への取り組み

リーンソフトウェア開発では、顧客価値の最適化を重視し、継続的な改善とフィードバックの活用が特徴だと思う。開発チーム全体で品質保証の役割を分散し、短いサイクルでリリースと検証を行う。これにより、顧客とのコミュニケーションを重視し、適応的な開発を実現する。
リリースサイクルは毎日で、検証を毎日行う。テストの自動化もマニュアルのテストもするが、リリースサイクルを短くすることで、最終的にユーザーが利用する物が高品質になることを目指している。
短いサイクルで品質を保証していくというのが特徴的だと思う。毎日リリースするので、細かい不具合は出るが、大きい不具合は見つかったらすぐに修正(リバート)される。品質が保証されずに安定しているとは言えないとも思う。場合によってはユーザーの活動が丸一日止まるようなこともある。そのため、ほとんどの場合、コンシューマー向け製品で採用されている方法だと思う。企業相手では、丸一日サービスが停止するような状況は到底受け入れられないサービスレベルだと思う。

両方を経験しての比較として

両者を経験したことがあるが、顧客のニーズが違うというのが最も大きな違いだったと思う。
ウォーターフォール開発の場合は、ハードウェアを作っている製品開発者などが顧客であり、ハードウェアが関係するので、リリース時期が動かせないのだ。
リーンソフトウェア開発は、いわゆるWeb系企業で、これらの企業の顧客は一般人で、リリースしたものが直接顧客に届くのが一般的だと思う。そして一般の人にはリリース時期を確約することはほぼないと思う。これは、どんな製品でもそうだが、ソフトウェア開発が直接的に製品になっている場合もリリース時期を確約をすることはまずない。

開発現場からするとどちらの方が働きやすいかと言ったら当然リーンソフトウェア開発なのである。
どんな開発者でも品質の保証など出来ないし、努力はするが保障というのは難しいのである。
リーンソフトウェア開発でも毎日完璧を求められることもあるとは思うが、ほとんどの場合は、一般のユーザーがほとんど無料で利用しているので、不具合が発生することはある程度織り込まれている。顧客との期待値の調整がすでに行われていて、結果として毎日のリリースなどが可能になっていると思う。
ウォーターフォール開発の最も深刻な問題は、保証できないものを保証しようとしているところだと思う。企業のニーズ(スケジュール死守)があるとはいえ、出来ないことを約束している点に問題があると思う。製造業においても不具合というのはあると思う。だが、製造ラインを動かす前に仮組みをして問題が発生すすれば、何処かの工程からやりなおすはずだ。
設計に問題があっても、必ずしも設計からはやり直さず、製造工程でなんとかすることもあるとは思う。
だが、ソフトウェアの不具合を製造工程で吸収するというのは、不可能だろうと思う。逆はあったとしても。そのため完璧なものが求められるが、最終的な製品になっていない状況で完璧はあり得ないと思う。

日本のSIerは多重下請けとかの問題もあるし、最近では給料もWeb系の方が高くなっていると思う。メーカーのソフトウェア開発も似たような状況だと思う。顧客からのニーズに応えているだけといえば、そうであるが、結果として産業全体で不可能なことを実現するために多くのリソースを割いているようにしか見えない。

ソフトウェアをハードウェアの部品のように扱うのではなく、ソフトウェアとして利用するようにユーザー企業が変わっていかない限りはこういった状況は変わらないのだろうなと思う。


2019年12月25日水曜日

Flutter advent calendar 2019 まとめ

やり始めたから最後までやったけど、25個はネタが無かったです。
むりやり作った感のあるものも何個かあったなと。。。
とはいえ、調べたことの供養の意味もあったのでまあ良かったかなと。
https://adventar.org/calendars/4869

他には多言語対応や画面回転時の挙動とか調べたいなと思っていたりはします。
Nativeアプリをつくるなら、BLEやNFCをどうするのかとかも思いますが、そこまでやるならNativeのActivityなりを別途起動して対応したほうが現実的な気もしています。

まとめはFlutterについてちょっと感じたことをメモ程度にまとめようかと思います。

Flutterの良いところは、いろんなところで記述があるので、あえて良くないところを上げてみようかと。技術的な良くないところというよりは、将来的な運用も考えたときの不安ですね。

Dartが人気ないなーと感じてます。
Dartの言語としての人気は、お世辞にもあるとは言えないと思います。言語自体が良いかどうかは僕にはわかりませんが、人気がないということは、使っている人がいないということです。
https://redmonk.com/sogrady/2019/07/18/language-rankings-6-19/
ランキング的には圏外のようですね。。。Advent calendarも過疎ってましたし。
Flutterで利用したアプリが売上がたって、人を増やそうとした時に、人員の増加は他の言語などよりも厳しいのではないかと思います。人が少ないですから。
Dart自体は、独自の文法ということは無いです。個人的にはObjective-Cの方が難しい。。。実際に触ってみれば特に困る人は少ないと思いますが、過疎っているものに自分の時間を使うというのは、なかなか勇気がいりますしね。

Flutter自体の普及に関してもまだまだな感じはします。
Androidが出始めのころも、いまのFlutterとちょっと似ていて、Productionでの開発経験がある人は殆どいないけど、趣味で触ってみている人たちがたくさんいる感じでした。
実際に業務で、いろんな端末でいろんなバグを踏みながら開発をしていた人というのは、運良くAndroidの開発の案件が社内であって、運良く担当していたといった具合だったと思います。そのため、Androidの開発経験がある人を中途採用しようとしても、趣味でやってい人は見つかっても、業務経験がある人は殆ど見つからなかったと記憶しています。
Flutterの現状も、ちょっと似ているかなと感じるところがあります。
10年前くらいもBlog書いたり勉強会で発表したりとかのエンジニア文化はありましたが、今はもっとそれが活発なので、すごい流行っているように見えても、実はProduction環境ではそんなに使われていない。というのが現状かなと、特に定量的根拠はありませんが感じるところです。

開発元のGoogleがFlutterで儲ける気がなさそうというのもちょっと気になるところです。
Flutterの開発元はGoogleですが、GoogleのプロダクトでFlutterを利用しているものがあるかというと、パッとは思いつきません。
https://flutter.dev/showcase
ここにあるように、すでに様々なもので利用されてはいますが、GoogleがFlutterで儲けてはいないと思います。今後も儲ける気があるのかよくわかりません。
現状のツールやドキュメントやビデオなど、お金とってもいいレベルで揃えているのに無料です。ある日突然、Googleがもう辞める。って言い出さないか不安になります。
Open sourceだからみんなでメンテすれば大丈夫。というのは少々ナイーブで、現実には、お金を儲けているプロダクトの基盤になっている技術だから、企業がお金を出して雇っているエンジニアを使ってメンテするわけなので、Googleにはなにか儲ける方法も考えていただきたいところです。もしくは、儲かっているプロダクトで使ってほしいです。
Open sourceも当たり前ですが、万能じゃないです。様々なゲームアプリ(たぶんモンストも)で利用されているCocos2dxもhttps://takachan.hatenablog.com/entry/2019/11/30/234321
こんな状況のようです。
数年前に僕もCocos2dxを使ってゲームアプリをリリースしましたが、当時はCocos2dxも最新のLibraryを導入したり活発に開発されていたので、隔世の感があります。
Flutterはこうならないことを祈ります。


とはいえ、実際に触ってみた感触としては、Productionで利用できると思いましたし、実際に海外(主に中国?)では、使われているようです。
今、新しいNativeアプリ作るなら選択するかなと思います。
まあ、完璧なframeworkやlibraryなど無いので、何を使うかは、その時々のチームの人員などによって妥協しつつ決めていくと思うので、現実には常にFlutterがオススメというわけではなく、すべてのモバイル環境でカメラや指紋認証も使えるようになりつつあるWebでのアプリ作成も選択肢に入れていくとは思いますが。

1人 Flutter Advent Calendar 2019:#25 onActivityResult


Androidでは一般的な問題ではありますが、OSが勝手にActivityをkillすることがあります。
Androidであれば、状態を保存しておき、再構築するための仕組みが用意されていますが、Flutterにおいてはそれがありません。

OSにActivityがkillされた場合にFlutterのinstanceも開放されてしまいます。stateの管理をどうすればよいのか?
これについては、すでにissueが上がっていて
https://github.com/flutter/flutter/issues/6827
ここで、議論はされていますが、3年前から続いています。。。
特にこれといったスタンダートになっている解決策はないというのが現状だと思います。

ただ、基本的にFlutterでアプリケーションを作成して動作させていて、問題になることは、ほぼないと言って良いと思います。アプリが裏にいって、また最初からでもそんなに困るケースはまれですし、そういうもんだという前提で作ればどうにかなるかと思います。

問題になるのはFlutterアプリケーションがほかアプリとアプリケーション間連携を行うような場合などだけかと思います。
カメラで撮った画像を取り込むとか何かのSDKを組み込んで値をonActivityResultで取得するのを前提とした仕組みの場合でしょうか。

すでにissueが上がっていることは示しましたが、解決策がないので自分でなんとかするしかないです。

onActiivtyResultがcallされたタイミングではFlutterのインスタンスがまだないので、値を通知することも出来ません。
Flutterの準備が出来たタイミングでFlutter側から値を取得しに行くほうが良さそうです。
そうすればタイミングを考える必要もほとんどないので。

とりあえずサンプルを作ってみました。いつものmy_appで数字をcount upして、中央のbuttonを押すとFlutterとは関係のないAcitivytが立ち上がります。このActivityで数字を編集して、Buttonを押すともとのFlutterのActivityに値を渡すというものになります。

普通に実行すると

I/flutter android(15154): enter onCreate
I/flutter android(15154): exit onCreate
I/flutter (15154): enter main
Syncing files to device Pixel 4 XL...
I/flutter (15154): before getActivityResult
I/flutter (15154): exit main
I/flutter (15154): after getActivityResult
I/flutter (15154): before openOtherScreen
W/ActivityThread(15154): handleWindowVisibility: no activity for token android.os.BinderProxy@e3ade16
I/flutter android(15154): enter onActivityResult
I/flutter android(15154): exit onActivityResult
I/flutter (15154): after openOtherScreen
I/flutter (15154): before getActivityResult
I/flutter (15154): after getActivityResult

こんな感じです。
openOtherScreenをcallするとActivityが立ち上がるのですが、それの結果が戻ってくるのをawaitで待っています。
で、onActivityResultが終わった後にopenOtherScreenのreturnが来ているのが分かると思います。
FlutterとAndroidのActivityの間でデータのやり取りがスムーズに出来ています。

端末の設定でDon't keep activityを有効にして同じことをしてみると。

I/flutter android(15383): enter onCreate
I/flutter android(15383): exit onCreate
I/flutter (15383): enter main
Syncing files to device Pixel 4 XL...
I/flutter (15383): before getActivityResult
I/flutter (15383): exit main
I/flutter (15383): after getActivityResult
I/flutter (15383): before openOtherScreen
W/ActivityThread(15383): handleWindowVisibility: no activity for token android.os.BinderProxy@189e8ec
W/ActivityThread(15383): handleWindowVisibility: no activity for token android.os.BinderProxy@75ebc8e
I/flutter android(15383): enter onCreate
I/flutter android(15383): exit onCreate
I/flutter android(15383): enter onActivityResult
I/flutter android(15383): exit onActivityResult
I/flutter (15383): enter main
I/flutter (15383): before getActivityResult
I/flutter (15383): exit main
I/flutter (15383): after getActivityResult

openOtherScreenをコールして他のActivityが立ち上がって、裏に回ったFlutterのActivityが一旦destroyされているので、またonCreateから始まっているのが分かると思います。
ここで、「exit onActivityResult」の後に「enter main」が来ていることからもonActivityResultで取得した値を単純にFlutterに受け渡せないのが分かると思います。

なので、Flutterが起動し終わった後にgetActivityResultをFlutterからcallして結果を取得するようにしています。
こうすることで、外部アプリとの連携なのでデータのやり取りが可能かと思います。
まあ、platformのつなぎこみとかでタイミング問題が出る時には、だいたい待ち合わせを頑張るよりかは、値をpullしにいく仕組みにしておいた方が、見通しが良い気がしています

サンプルは以下です。
https://github.com/matsuhiro/on_activity_result_test

2019年12月24日火曜日

1人 Flutter Advent Calendar 2019:#24 無限scroll list + checkbox


FlutterのLIstViewは予めitemの数がわからなくても作成できます。
nullを返すまでは作り続けることができます。

checkboxをつけた時にどういった動作になるのかなどが気になりました。
内部ではWidgetがリサイクルされているので、checkboxのcheckがスクロールアウトしてもう一度表示したときには消えてしまいます。
checkされていたというデータをどこかに保存する必要があります。

例ではsharedPreferenceに入れてみました。
https://github.com/matsuhiro/scrollable_list_radio_test

適当にitemに表示する文字列をユニークキーにしていますが、ちゃんとItem用のデータ構造を作って、unique keyはindexにするとか決めたほうが良いとは思います。

ListViewでもFutureBuilderは、きちんと使えそうなので、DBから表示すべきデータを読み込んでから出すとか、通信の結果として得られたものを表示するとかも問題なく出来そうです。

FutureBuilderを利用しなくても、asyncでデータが得られたあとでsetStateすれば同じことはできますが、その場合にはcheckされていないものがcheckされた状態になる時に、checkboxのところだけ少しちらつく感じがするので、FutureBuilderを利用して全体的に差し替えた方が結果的にはきれいに表示される印象です。

Listに関しては、これが良い記事でした。実際に直面した課題の解決策は読んでいて面白いです。
https://note.com/kitoko552/n/n52cb06e79d49

2019年12月23日月曜日

1人 Flutter Advent Calendar 2019:#23 flare&lottie


Effectを再生するにあたって、AndroidやiOSをやっていた人ならば、Lottieを思い浮かべるのではないでしょうか?Flutterの場合はFlareというのがあります。

https://www.2dimensions.com/
再生用のlibraryによって使い方が異なるので、どっちが良いかと言うのは言えないですが、どちらも良く出来ています。

どっちも、再生用に利用されるファイルはJsonです。
サンプルを記載しておきますが、AndroidのSimulatorではLottieの再生は出来ませんでしたが、実機であれば問題なく再生されました。

Lottieについては、FlutterのPlatformVIewを利用しているようです。
そのため、iOSでは再生できるけどAndroidでは再生できないとかありそうです。
Flareの方は、dartで処理しているようだったのでそのようなことは無さそうです。

とはいえ、Flutterは内部ではSkiaを利用されています
https://skia.org/dev/flutter
skiaには
https://skia.org/user/modules/skottie
といった感じで、Lottie用のI/Fが用意?されているようです。(ただしフルサポートしていいるのはまだAndroidのみ)
そのため、こちらで再生する形のものを作った方が良いかも?

以下にサンプル上げておきます。
https://github.com/matsuhiro/flutter_flare_lotie

2019年12月22日日曜日

1人 Flutter Advent Calendar 2019:#22 animation


Animationをする場合に、途中で止めたくなることあるとありますかね。
ゲームだと当たり前に途中で止めることあるんですが、Flutterが想定しているようなアプリの場合はあまりないかも。

でも気になったので調べました。
stopで簡単に止まりました。再開しようと思ったら、forwardを呼び出せば再開します。
簡単でした。




レイアウトも動的に変わるし、とても簡単です。
コメント入れてありますが、試すときにはsdkのversionを2.2.2以上にしてください。
https://github.com/matsuhiro/animation_test

floatingボタンで開始/一時停止が行えます。

2019年12月21日土曜日

1人 Flutter Advent Calendar 2019:#21 通信状態監視


通信状態を監視して、状態を変えて、ユーザーに操作を促すとか普通にやりたくなると思います。
Snackbarだしたりとかとか。

それもFlutterだと簡単に出来ます。
Flutterのいいところは、誰かが作ったものがたくさんあるところですね。Androidとかも同じなんですが、導入までのハードルが低すぎて引きます。

https://pub.dev/packages/connectivity
というのを使ってみました。

これ使うと通信状態監視できます。
サンプルは以下です。
https://github.com/matsuhiro/flutter_connectivity_test
airplane modeとかにすれば、snackbar出てくると思います。

ちょっと工夫したところとしては、Snackbarを表示するところでしょうか?
setStateでUIが変わるんですが、そこでSnackbarを表示しようとするとエラーになるため、Delay 0秒で、UIの構築の次の処理に回しています。