のんラボ

BIツールとは?開発するには何を実装したら良いのだろう?

2019/09/26 2019/10/02

売上分析機能の仕様を考えていたときにふと見かけた単語 こんにちは。Nonです。 今回は技術というより、業界のお話になってしまうかもしれません。 プロフィールにあるように僕は株式会社スマレジに所属するエンジニアです。 現在、僕が関わっている案件の中で売上を分析する機能に関わる仕様を考えています。(このブログやその内容は、組織を代表する意見・主張ではありません。) とはいえ、僕はIT業界以外でガッツリわかる他業界のことといえば医薬業界のことのみで、この業界のような金勘定系の分析はあまり詳しくなかったのです。 とりあえず、Google先生に売上分析とは何かを教えてもらっている中で、個人的に気になるワードがあったので調べてみました。 BIツールっていう単語 BI(ビジネスインテリジェンス)ツールというらしいです。 引用:https://data.wingarc.com/what-is-bitool-6123 企業に蓄積された大量のデータを集めて分析し、迅速な意思決定を助けるのためのツールです。 経営管理や売上のシミュレーションなどに活用できるもので、近年BIツールを利用する企業が増加しています。 だそうです。 この単語の意味することを知ってちょっと考え方を変えて見ました。 今までは 売上分析という機能を作る という考え方で進めようとしてきましたが、 BIツールを作る という考え方にシフトしたほうが良さそうだったからです。 現に、売上分析機能を作る目的は売上のシミュレーションなどに活用していただき、経営判断を支援することだからです。 これはBIツールの考え方と同じで、僕はBIツールに限りなく近いものを作っているんだな。と考えたからです。 では、 一般的なBIツール BIツールを作るためにはどうしたら良いのか? BIツールに搭載すべき機能は? について纏めて行きたいと思います。 一般的なBIツール ダッシュボード BIツールですので、この機能は必須です。 ダッシュボードとは、その名の通り、車のなどの速度計や油圧計など、操作に必要なものを「わかりやすくグループで纏め、見える化したもの」を指します。 本部機能で言えば、 今月の売上推移 今月の会員推移 今月の在庫推移 などが閲覧することができれば良さそうですね。 しかしこのダッシュボードを一番最初に目に入れることになりそうなので、他社商品とは一味違ったものを実装したいものです。 レポーティング この機能を見たときに「確かに」と思いました。 売上分析機能を作ることばかりに集中してしまって、ドキュメント化できるようにしたり、資料の様にまとめる機能のことをすっかりと忘れていたからです。 要は、売上分析した結果や、貯めたデータの見える化機能です。 関連・類似する機能として、 ダッシュボード PDF等出力機能 があります。 紙にする機能はやはり必須でしょう。 しかしそのためにPDF機能を搭載するのは開発・メンテコストが高くなりそうです。 print用のCSSでなんとか対応したいところですね。 イメージとしてはダッシュボードをWEBページとして表示し、それをそのまま印刷できるようにすることでしょうか。 近年ではGoogle Analyticsも普及してきていますのでUI/UXはそのツールに近くすれば、良いかもしれません。 分析機能 一言で分析機能といっても色々な分析手法があります。 こちらのサイトがとてもわかりやすく纏めてくださっているので、参考になりました。 https://www.albert2005.co.jp/knowledge/marketing/customer_product_analysis/abc_association ABC分析などの数学的分析手法 アソシエーション分析などの連関分析手法 データマイニング これらを搭載したBIツールであれば、かなり有用なソフトになるのでは無いでしょうか。 また、これらを踏まえた上で、これからの分析に外せない注目機能があります。 AI分析 これですね。 回帰分析などを含む統計的な数学分析を行うことで、未来を予測しようという機能があります。 今までは、数学的な公式などを単純当てはめているだけの機能や、自作して頑張っている機能などを見たことはありますが、一般的なアプリで未来を予測するアプリといえば天気予報しか見たことがありません。 しかし、現在は違います。機械学習用ライブラリが簡単に手に入る時代です。 しかもそのライブラリは数学に詳しい方々が、すでにある程度のチューニングを終了させているものなので、自作するより制度が高いまであります。 (これを聞くと自動車のMTとATをイメージしてしまいますね。) システム的にも運用が止む夜間に前日までのデータを解析し、実際に使用される昼にそのデータを返すロジックにしておけば、サーバー負荷的にも問題ないように思えます。 そもそも、解析用サーバーを一台用意しても良いかもしれません。 解析ソフトとしては、Facebook製のprofetが大変使いやすいと、聞きます。 しかも、よくある解析手法である時系列解析が得意だそうです。(売上分析にピッタリではないでしょうか?) ほんの少しの懸念点 それはチューニングです。 ものを売るときに年末セールや、休業などを挟んでしまうと、解析データの取得方法を変更したり、学習方法のパラメータを変更する必要があります。(年末休業のせいで、2月の売上予想が下方修正されることになっては困りますしね。) 自社の売上解析をする場合はパラメータを自由に設定したり、データのマイニング方法を変えたりすることができますが、これを提供する側になると、パラメータ設定はユーザーにしていただく他ありません。 しかし、普段の業務などで忙しく、その道の専門家ではない方々に、そのあたりの設定をうまく扱えるのでしょうか? そのパラメータ設定などを補助するツールをこちらで作成出来るかどうかが、少し大変そうです。 まとめ 本部機能には売上分析機能を強化というよりも、BIツールの側面を強く反映すれば良いのではないか? AIや統計で未来予測をすることができれば、みんな見てくれるのでは? 会議用にドキュメント作成も充実できたらいいよね 最後に いかがでしたでしょうか? これは僕の意見ですので、商品に反映されるかどうかはまだわかりませんが、解析や分析はプログラムの最も得意とすることです。 仕事でできれば、とてもやりがいがありそうなので楽しみですね。 手探りになりそうなので、ノウハウや知見をお持ちの方はコメントしていただけると、泣いて喜びます!! そのときはよしなに。 .

Vue cli のserveコマンドで立ち上げたときにlocalhostへのアクセスが遅い現象について調査しました。

2019/09/25 2019/09/25

vue-cliのserveコマンドでlocalhost接続するとめちゃくちゃ遅い現象 こんにちは。Nonです。 今回はVueの開発中にハマったことについて、軽く纏めておこうと思います。 vue-cliでVue環境を構築し、あるアプリを作成しようとしていたのですが、レスポンスが重く作業にならない現象がありました。 結論 vue.config.jsに下記の設定を追加してください。 copied.module.exports = { devServer: { port: 8888, disableHostCheck: true, }, }; 原因 ポートか名前解決が原因かと思います。 私の環境ではサーバーサイドの開発のために、dockerを起動しながら開発をしていたので、ポート周りで激突した可能性があります。 disableHostCheckはホスト名ではなく、IPで接続できるようにする設定だそうです。 なので、localhostではなくIPでローカルと接続します。 うーん...名前解決が原因なのでしょうか? 色々調べてみた Macの名前解決について調べた https://kuroeveryday.blogspot.com/2018/08/accessing-to-localhost-is-very-slow-on-high-sierra.html 127.0.0.1でアクセスする(解消) 詳細は後述するが、どうやらlocalhostの名前解決に時間がかかっているようだった。そのため、127.0.0.1でアクセスすると正常にアクセスできた。 とあるので、こちらもlocalhostで接続するのではなく、IPで接続する手法で解決しています。 もしかすると、MacOSとChromeの相性な気がします。 ブラウザを変えてみる MacOSとChromeの相性が原因なら、ブラウザ変えたらどうなるのでしょうか。 Chromium系ブラウザで現象が起こるので、safariとかにしてみました。 結果:めっちゃ速い これは確定じゃないですか? Vueでめっちゃ速くなったので、今度Dockerのlocalhost接続時もSafariで接続してみて早くなったらもう確定でしょう。 もしかしたらポートは関係無い? copied.module.exports = { devServer: { port: 8888, disableHostCheck: true, }, }; と、書きましたがport: 8888は必要無いかもしれません。 私の環境ではこれが無いと遅くなったので必要でしたが、他の方の環境だと必要無いかもしれません。 その時はコメントなどでご教示頂ければ幸いです。 最後に 今回はvue-cli使用時にlocalhost接続が重くなる現象について纏めてみました。 しかし、ほとんどが名前解決のお話になってしまいましたね。 MacOSとChromeを使用してlocalhost接続をした際に、重い現象に当たったら是非参考にしてみてください。 なにか知見のある方がおられましたら、コメントなどに書いてシェアしていただけると大変助かります。 .

composerでLaravelにパッケージを追加する

2019/07/15 2019/09/10

composerでLaravelにパッケージを追加する こんにちは。Nonです。 今回は前に投稿した記事、 Laravel5.7からの新機能「Telescope」について | のんラボ Laravelの新しいデバッグアシスタント「Telescope」こんにちは。Nonです。Laravelの新デバッグ機能「 ... でTelescopeを導入したときに少しハマった内容をシェアしたいと思います。 初めに composerでパッケージをインストールするときに一番始めに知ることといえば、 copied.composer require アカウント名/パッケージ名 --dev ですね。 上記コマンドを叩くと、 copied."require-dev": { "hoge": "hoge" } 上記のようにcomposer.jsonの"require-dev"にパッケージが追加された状態になります。 もちろんcomposer.jsonを直接編集する場合もあるでしょう、 要はcomposer.jsonが copied."require": { "foo": "foo" }, "require-dev": { "hoge": "hoge" } となっていることが重要です。 またこのとき、 "require"が必須パッケージ。"require-dev"が開発環境用のパッケージとなっていることにも注目です。 当初僕は、"require"と"require-dev"が別の区切りを持つものと勘違いしておりました。 正しくは"require-dev"が"require"を包含するイメージですね。初めて触る人でこのような勘違いは割とあるのでは無いでしょうか。 (adsbygoogle = window.adsbygoogle || []).push({}); インストール "require-dev"が開発環境用のパッケージ郡なので、開発環境のみ必要で、本番環境にはいりません。"require"はプログラムを動作させるのに必須のパッケージ郡です。これは必須のパッケージ群なので本番環境と開発環境両方に必要ですね。 ここで開発環境に"require-dev"を含む"require"をインストールする方法と、本番環境に"require"のみをインストールするコマンドがあります。 開発環境 開発環境のときはこのコマンドを実行します。 copied.composer install --dev composer.json copied."require": { "foo": "foo" }, "require-dev": { "hoge": "hoge" } 上記のようにcomposer.jsonに登録されているものがインストールされます。 本番環境 本番環境のときはこのコマンドを実行します。 copied.composer install --no-dev composer.json copied."require": { "foo": "foo" } 上記のようにcomposer.jsonに登録されているものがインストールされます。 アップデート copied.composer update もinstallと同じ様に--no-dev と--devが存在します。 composer.jsonを更新したときに実行するコマンドです。 ユースケース 開発環境に初めてインストールする時 copied.composer install composer.lockにかかれている内容がインストールされます。 開発環境に新しいパッケージを追加する時 copied.composer require アカウント名/パッケージ名 composer.jsonに新しくパッケージが追加され、composer.lockが更新されます。 開発環境のパッケージを最新版にしたい時 copied.composer update パッケージの依存関係を解決し、composer.jsonとcomposer.lockを更新します。 本番環境に反映する時 copied.composer install composer.lockにかかれている内容がインストールされます。 また、基本的に copied.composer update は本番環境では実行しない方が良いでしょう。 開発環境で実行し、問題が無いことをテストをしてから本番環境にアップロードし、 copied.composer install しましょう。つまり本番環境は copied.composer install のみを実行するのが無難です。 最後に 今回はcomposerについて纏めてみました。 どちらかといえばハマった自分への備忘録として書いたので、皆さんはすでに知っていることばかりだったかも知れません。 Telescopeを導入するときに、本番環境でcomposer updateをしてしまったせいで、大変な目に合いました。 Telescopeをレンタルサーバーに導入しようとすると、PHPモジュールが足りないせいでエラーが発生し、サーバーエラーで環境がぶっ壊れます。 うーんこれは、一応今度記事にするかも知れません。 (レンタルサーバーでアプリ開発している人はいないだろうから、しないかも知れませんが、、、) その時はよしなに。 .

UIデザインについて少し調べてみた。

2019/08/28 2019/09/06

個人開発したアプリでめちゃくちゃダメ出しされました。 こんにちは。のんです。 今回はデザインについて書いていこうと思います。 デザインと言っても、美的なデザインや、商品紹介などの画像配置のデザインではなく、どちらかといえば機能美を中心に書いていこうと思います。 と、言うもの、友達にテストというか、使ってみてもらって、開口一番 使用方法がわからない と言われました。 いやぁ...参りました。 いくら機能に力を入れても使用方法がわからなければ、なんの意味もありません。 仕事で機能を考えるにしても、画面ベースで考えることが多く、どの機能をどこに配置しようかを考え、画面作成していると「あれもいるな」とか「これはいらないな」とか後からどんどん出てきて困った困った。 これは一度デザインの定石を知っておく必要があるなと思い、畑違いではありますが、記事にしようと思いました。 定石ではありますが、 やっぱりそうだよな。 言っていることは簡単だけど実際にはできていないな。 そういう考えがあるんだな。 と思った、印象深い3つの考えを書こうと思います。 参考書は Design rule index です。 少し古めの本ですが、古いからこそ昔からある「定石」や、こういう考え方もあるのかという閃きのための材料にもなりました。 オッカムの剃刀 機能的に同等なデザインの中では、最も単純なデザインを選ぶべきであるという原則です。 この原則は、デザインの分野において、複雑さよりも単純さが好ましいという意味になります。不要な要素はデザインの効率を損ない、予測できない結果が生じる可能性を高めるという考え方が暗に含まれています。 一番の例はGoogleの検索フォームです。 どっちが使いやすいでしょうか? 答えは明白かと思います。 同じ検索エンジンを積んだサイトのはずですが、使用率はまるで違います。 検索サイトシェア Google74.92% Yahoo21.92% Bing2.33% Other0.83% 引用元:検索エンジン別日本のシェアを調べてみた2018年版 他のインターネット検索サービスが「広告」や「特別な機能」を争って追加している中、Googleは単純で効率重視のデザインを貫徹しています。 この結果、業界TOPのシェアを誇る検索サービスとなっています。 歴史上の偉人たちの言葉からもこれは読み取ることができます。 自然は可能な最短距離を選ぶ    アリストテレス 他の条件が同じならば、要求が少ないものの方が良い    ロバート・グローステスト 良い連続 人間は1列に纏められた複数の要素は、1つのグループあるいは塊として認識します。 細切りにされたアニメーションをパラパラ漫画の様に映し出したら、人間はその隙間を埋め、複数の絵の塊を動画として認識します。 見やすいグラフ見にくいグラフ 上記のグラフを見比べても前者のほうが見やすいのは、並びが線形で、1つのグループとして見ることができるからです。 また、シマウマなどが複雑な模様をしているのも、群れた時にグループ化し、捕食者から、狙いを絞りにくくする効果があると言われています。 科学的根拠は取れていませんが、この群れの中から的を絞るのは至難の業ですね。 同じ様に斑模様のある動物にも言えそうですね。 順列 線形 群集 この3つをうまく並べて見せることで、バラバラな情報を1つにまとめあげて直感的に伝えることができそうです。 ここでは大きく取り上げませんが、階層の考えかたも同じかもしれません。 ツリー構造 ネスト構造 階段構造 すべてが、似た要素をグループ化しています。 ワーディング 現代日本人の「言葉の裏」を読む能力。つまり「相手を察する」ことで意思疎通をはかる能力は非常に高く、「ハイコンテクスト」なコミュニケーション文化だと言われています。 ハイコンテクスト ハイコンテクストとは、コミュニケーションや意思疎通を図るときに、前提となる文脈(言語や価値観、考え方など)が非常に近い状態のこと。民族性、経済力、文化度などが近い人が集まっている状態。 コミュニケーションの際に互いに相手の意図を察し合うことで、「以心伝心」でなんとなく通じてしまう環境や状況のこと。「ハイコンテクスト文化」や「ハイコンテクストな社会」などとして使われる。 日本の文化は、「空気を読む」などのように文脈理解が重視されるハイコンテクストな文化とされる。 しかし一方で、「語彙力」や「ライティングスキル」が著しく低下していることも指摘されています。新時代の文化など時代の流れも影響していると思われますが、一般的な文書として若者言葉などは使用できません。 それだけではなく、パソコンやスマートフォンなどの普及によって「コピペ文化」となってしまい、イケてるデザインをコピペするだけで、自社のキャッチを「自社でライティング」することができなくなってきているとも言われています。 システムにおけるワーディング 「一言で相手に伝えたい」 これを考えるときは映画や小説の「キャッチフレーズ」とは違います。 注目を集めるかどうかではなく、 より簡潔に 相手にわかる言葉で 活きた言葉で 伝えることでは無いでしょうか? 最後に ありきたりな内容ではありますが、機能実装のプログラミングをしている最中はロジカルな思考になってしまって、ついエラーメッセージをそのまま表示しようとしたり、エラーコードを数字を表示しようとしてしまいがちですが、そんなのユーザーにはわかる訳ありませんよね。 グラフなどもAPIから取得した内容をそのまま表示するわけではなく、ある程度見やすく理解しやすい状態で表示してあげたほうがページを利用してくれるかも知れません。 機能重視のアプリを作成していると機能の作成で頭が疲れてしまって、画面表示に思考を割くことが疎かになってしまいがちですが、この辺もきちんとできればいいものに仕上がるかも知れません。 もっともっとボタンなどの基礎UIについて書こうと思っていましたが、本を読むといろんな専門知識が出てきますね。 気になった3つを纏めてみました。 トレンドなどもあると思いますので、記事更新はちょいちょいしていきたいと思います。 参考書 Design rule index―デザイン、新・100の法則 | William Lidwell, Kritina Holden, Jill Butler |本 | 通販 | Amazon

来ましたね!Laravel 6

2019/09/04 2019/09/04

中身の無い記事で申し訳ありません!! こんばんは。Nonです。 どうやらLaravel 6がリリースされたようですね。 https://laravel.com/docs/6.0 公式ニュース https://laravel-news.com/laravel-6 更新内容 以下、https://laravel-news.com/laravel-6より引用。翻訳は拙い英語力なので、できるだけ許してください。 承認レスポンスの改善 これまではユーザーへの認証のカスタムエラーメッセージを作成するのは困難でしたが、Gate::inspectで許可ポリシーの応答を返すメソッドが用意されるらしいです。 copied.$response = Gate::inspect('view', $flight); if ($response->allowed()) { // User is authorized to view the flight... } if ($response->denied()) { echo $response->message(); } これはかゆいところに手が届いた感ありますね。 ジョブミドルウェア 正直全然わからない。 使ったことないです。 ミドルウェアを介してジョブを実行できる機能らしい。 copied.// Add a middleware method to a job class public function middleware() { return [new SomeMiddleware]; } // Specify middleware when dispatching a job SomeJob::dispatch()->through([new SomeMiddleware]); 遅延コレクション これもほしい機能でした!!! メモリを消費したくない弱い環境を使用している僕にはピッタリですね!! モデルのLazy Loadのコレクション版だと思われます! 詳細はこちら Laravel UI これは今のフロント流行の中でどの様に便利になっていくかとても気になります。 copied.composer require laravel/ui php artisan ui vue --auth github: https://github.com/laravel/ui Document https://laravel.com/docs/6.0/frontend ぜひ使ってみたいですね。 Laravel UI などは外部として用意されているのでしょうか? バージョンなどの依存関係も気になるところ。 最後に とりあえず、インストールしてみましょうか! 英語で具体的な操作まではわからないかも知れませんが、とにかく弄ってみることから始まりそうです。

Z250で初!キャンプツーリング!

2019/08/11 2019/08/11

洞川キャンプ場へツーリングしてきました こんにちは。のんです。 今回は久々に技術とは全く関係ない記事を投稿します。 週1ペースで技術系の記事を投稿していますが、月1でこういうのもいいと思っています。 毎回技術記事だと、どうしても固くなっちゃいますよね。 今回は、洞川キャンプ場へ行ってきました。 こちらは朝6時ごろに撮影した木漏れ日の様子です。 キャンプツーリングは初めて キャンプそのものも実は初めてだったはず。 バイクには「ネイキッド」「アメリカン」「スーパースポーツ」など色々な種類というか、フォルムがあるのですが、Z250はそのどれにも当てはまらない「ストリートファイター」というフォルムをしています。 イメージでいうと、「ネイキッド」+「スーパースポーツ」という感じです。 性能やパワーはスーパースポーツ並の性能を持っていますが、走行速度ではなく外見を意識しているので、カウルやヘッドライトの形状が違ってきます。 そんなZ250はヘッドライトが独特の形状をしていて、カウルもZ型にくり抜かれ、お世辞にも高速走行向けとは言えません。 何が言いたいかと言うと、ネイキッドとスーパースポーツの悪いとこ取りして、外見にステータスをぶっ放してます。(褒め言葉です。) なので、 積載性皆無!! なのです。 でも、いい体験できました。 普段、PCの画面ばかり見つめているせいか、自然と触れ合って、いい気持ちになりました。 昼は流石に暑かったですが、16時ごろになると急に涼しくなってきて、20時にもなると少し肌寒いくらいでしたね。 クーラーみたいな作られた涼しさではないので、とてもいい気持ちでした いい雰囲気も出てきて、友達とも少しセンチメンタルなお話ができたので満足です。 装備品の失敗 テーブル ランタン この辺が足らなくて夜少し不便をしました。 特にランタンは無いと真っ暗(真の闇です)。 火の始末が大変でした。 しかし、友達の知恵を借りて即席ランタンを作りました! 良い光だ・・・ 正直ランタン忘れてきてよかったと思うくらい照明の気持ちよさありましたよ。 皆さんも夜の光に困ったら使ってみてください。 ペットボトルの下にライトを点けたスマホを下敷きにするだけです。 いい旅でした 木漏れ日の写真もいい写真ですが、友達が内緒で撮ってたこの写真も好き。 来週はテスト駆動について書くつもりです。 この盆にテスト駆動について勉強して、実務的な内容を中心に1本記事にしたいと思います。 テスト駆動開発 | Kent Beck, 和田 卓人 |本 | 通販 | Amazon この本を手に入れてからずっと読もう読もうと思っていましたが、積んでしまっている状態なので、これを期に一気読みしようと思います。 最近、自分もテストをいっぱい書くようになってきて、テストコードを先に書く重要性みたいなものに気づいてきました。 その辺の自分の考えている実装方法についてもまとめてみようと思っていますので、そのときはよしなに。 最後に・・・ バイクは良いぞ! .

あなたは「何回」入力チェックをしているだろうか?

2019/08/04 2019/08/04

入力チェックは簡単でしょうか? こんにちは。Nonです。 今回は入力チェックについてまとめてみようと思いました。 入力チェック。意外と難しいというのが僕の認識です。 数字チェックや文字チェックだけかと思いきや 実はそうじゃない ところが難しい。 というのも、あるプルリクの時 Modelでバリデーションするんやで(CakePHP使用) Laravel使いの僕「!?!?!?」 となりました。 気を悪くしたらすみません。他意はありませんが、本当にこんな感じでした。 これが妥当なのかどうかを記事を書きながら検証していきます。 僕はセキュリティの最先端を行くエンジニアではないので、 入力ってなんや? 入力チェックってなんや? 入力チェックって何回すればええんや? 遠回りにはなってしまいますが、 「 あなたは何回入力チェックしているだろうか? 」 この問いに答えるために、上の順番で追ってみようと思います。 (激しい議論になりそう😂お手柔らかに・・・) 入力ってなんや? これも経験値がある人にとっては簡単な質問かもしれません。 入力とはプログラムに送信される値であり、パッと思いつくのは2種類です。 妥当な入力 異常な入力 ここで1つ問いを投げたいと思います。 入力ミスは異常な入力であるか? 答えは否です。 中には不思議に思われる方もおられるかもしれません。 しかし、入力ミスは 妥当な入力 です。 「妥当な入力」ってなんや? 妥当な入力とはエンジニアが想定した値であり、プログラムが正常に動作する値 です。入力ミスはエンジニアにも想定可能であり、入力された時は入力エラーとして画面に表示します。 例: あるアプリで商品の値段を登録する画面があります。 商品名は文字列で、値段は正の整数を想定しています。 用意されたDBは下記の通りです。 products id AUTO_INCREMENTname VARCHAR(100)price INT 用意されたプログラムはPHP(Cake風)です。 copied.public function add() { // その他コード if ($this->request->is('post')) { // 登録処理 $entity = $this->products->newEntity($this->request->data); $this->save($entity); } // その他コード } 入力されたデータは 商品名→おにぎり 値段→ -100 でした。 -100は異常な入力でしょうか? 違います。 これは妥当な入力です。 プログラムは正常に動作し、productsテーブルのpriceに-100を登録することでしょう。 しかしこれでは仕様を満たせません。 だからこの入力は 入力ミス です。 エラー画面に遷移し、「正の整数を入力してね」って伝えるために、プログラムで入力チェックし正の整数のみ許可します。UNSIGNED使えや。 「異常な入力」ってなんや? 対する異常な入力とは プログラムが正常に動作しなくなる 入力です。 エンジニアの想定。つまり設計に反する入力です。先程の例をもう一度利用して見ましょう。 例: あるアプリで商品の値段を登録する画面があります。 商品名は文字列で、値段は正の整数を想定しています。 用意されたDBは下記の通りです。 products id AUTO_INCREMENTname VARCHAR(100)price INT 用意されたプログラムはPHP(Cake風)です。 copied.public function add() { // その他コード if ($this->request->is('post')) { // 登録処理 $entity = $this->products->newEntity($this->request->data); $this->save($entity); } // その他コード } 入力されたデータは 商品名→おにぎり 値段→ 百 でした。 百は異常な入力でしょうか? そのとおりです。 これは 異常な入力 です。 数字のフィールドに文字は入りません。 つまり、 プログラムが正常に動作しません。 type="number"使えや。 参照系の入力 もうちょっと例を上げてみましょう。 例: URL: https://hostname/edit/{:id}を想定した編集ページがあったとしましょう。 productsの内容は下記の通り。 idnameprice 1おにぎり100 正常入力は https://hostname/edit/1 入力ミスは https://hostname/edit/2 異常入力は https://hostname/edit/あ ですね。 copied.public function edit($id) { $product = $this->products->get($id); if ($this->request->is(['post', 'put', 'patch']) { // 更新処理 } $this->set(compact(['product'])); } https://hostname/edit/あでアクセスされた時、きっとView側でエラー出まくりでしょう。 copied.public function edit($id) { // idの数字チェック $product = $this->products->get($id); // productの存在チェック if ($this->request->is(['post', 'put', 'patch']) { // 更新処理 } $this->set(compact(['product'])); } コメントで追加した、idの妥当性チェックと、productの存在チェックが編集画面では必須ですね。 意地悪して攻撃してくんな セキュリティという観点から見てみる そもそも処理を行う前に入力チェックする理由は何でしょうか? 決まっていますセキュリティを上げ、攻撃の対策するためです。 初心者どころか、プログラムを知らない人でも思いつきそうな答えですね。 SQLインジェクション コマンドインジェクション XSS 数々のコマンドやクエリの仕様を利用して悪意ある攻撃をしてくる輩もいるでしょう。 その対策としてエスケープがあります。 これは入力チェックでは無いところに注意してください。(知識つけたら記事にするかもしれません。) 入力の種類 これまでの例から、入力はいくつかの種類で分けられそうですね。 妥当な入力 正常な入力 入力ミス 異常な入力 バグ 攻撃 ではこれらの種類から正常な入力のみを許可する入力チェックをしましょう。 入力チェックってなんや? そもそも前提として入力チェックは入力値が妥当であるかをチェックします。 入力チェックはホワイトリスト?ブラックリスト? セキュリティを専門とする業界では、妥当であるかを判断するには、ホワイトリストとブラックリストどちらがいいのかという内容で激しい議論になっているそうですが、少なくとも私はホワイトリストだと思っています。 例: あるテキストフォームは 16文字以上 という入力チェックを行う時、 15文字以下をエラー 16文字以上を処理 これは難しいですね。どっちもあってる気がします。 では少し複雑にして 例: あるパスワード入力フォームで 英字と数字を含む16文字以上 という入力チェックを行う時、 ひらがな、カタカナ、漢字、記号を含まない15文字以下を不許可 英字と数字のみの16文字以上を許可 と考える時、わかりやすいのは②です。 「わかりやすいから」が理由?そうではありません。 ホワイトリストのデフォルト状態って何だろう? なぜホワイトリストなのか? ホワイトリストのデフォルトは「すべてを許可しない」だからです。最強の入力チェックです。プログラムに何も送らない状態がデフォルトですから。 対するブラックリストは「すべてを許可する」から許可しないものを指定します。これはセキュリティに穴がありそうなのが感覚的にわかりますね。 ではなにをホワイトリストに登録していくべきなんでしょうか? ホワイトリストに登録するためにバリデーション(検証)する ここでようやく出てきますね。 皆さんご存知のバリデーションです。 バリデーションってなんでしょうか?辞書を引いてみました。 バリデーションの意味 バリデーション [3] 【validation】 〔確認,承認の意〕 ① 妥当性確認。検査・分析の方法やその作業プロセスなどが適切であるか科学的に検証すること。 おお。妥当性確認ですって。 入力の種類でも挙げました。 妥当な入力 正常な入力 入力ミス 異常な入力 バグ 攻撃 バリデーションにも色々種類がありそうです。 入力の種類だけ検査にも種類があるでしょう。 バリデーションの種類 結論から言いますと、3種類ありそうです。 -チェック内容 入力データバリデーションプログラムが正常に動作するかどうか。つまり妥当な値かどうかを検証する。 ビジネスロジックバリデーション仕様を満たすかどうか。つまり正常な値かどうかを検証する。 出力バリデーション出力した結果、他者に害をなさないか検証する。 妥当な入力か異常な入力かどうかを「入力データバリデーション」で検証。 郵便番号に何故かひらがなが入力されている 正常な入力かバグかどうかを「ビジネスロジックバリデーション」で検証。 都道府県が「大阪」なのに、郵便番号はどう見ても「東京」の番号 という感じですね。だんだんタイトルへの回答の材料が揃ってきました。 では、出力データバリデーションとはなんでしょうか? 「セキュリティという観点から見てみる」でありましたが、XSSがこれに含まれそうですね。 自分(プログラム)には影響は無いが、利用者に迷惑を掛ける入力データがあります。 もちろんXSSだけでなくAPI利用している場合、変な情報を与えられても困りますしね。 例: API利用の際、 copied.{ "name": "おにぎり", "price": "-100" } こんなデータ寄越されても「どないせぇっちゅうねん」って感じですね。 なので出力バリデーションは出力した結果、他者に害をなさないか検証するバリデーションです。 入力チェックって何回すればええんや? 何回でもせーや そもそもセキュアコーディングの定義のうちの1つとしてこういうものがあります。 Implement multiple layers of validation. (多層のバリデーションを実装していること) 入力バリデーションで妥当性のある値を使用してプログラムを落とさない様に ロジックバリデーションで仕様上バグのでないシステムに 出力バリデーションで他人様に迷惑をかけないように この多層構造がよりアプリのセキュアにします。 (より正確に言うならセキュアコーディングの要素のうちの1つなので、まだまだ脆弱性はあります。) Modelでバリデーションするんやで(CakePHP使用) これは不正解ではありませんが、足りないということですね。 同様にLaravel使いの場合 FormRequestでバリデーションするんやで(Laravel使用) も足りないということです。 プログラムを動作させる時は 入力バリデーション ビジネスロジックバリデーション データ処理 出力バリデーション レンダリング この流れが基本ということになりますね。 最後に バリデーション。調べてみると奥が深くて脱帽しました。 調べ始めはいろんなブログ記事で バリデーション(の実装)とは??(小並感) みたいな感じでしたが、セキュリティに詳しい方のページに言ったらとんでもない議論の嵐になっていてとても勉強になりました。 7ペイなどの事件もあり、このタイミングでセキュアなことを知れてよかったと思います。 もっともっと、知見のある方いらっしゃるかも知れません。この記事をきっかけにセキュリティ意識が高めれば幸いです。 参考 書かない日記(https://blog.ohgaki.net/) バリデーション以外にもとても勉強になりました。ありがとうございます。

POSレジの本部機能について少し調査してみました(金銭編)①

2019/07/28 2019/07/28

はじめに こんにちは。株式会社スマレジでサーバーサイドエンジニアとして働いています。Nonです。 働くうちにふとしたきっかけで本部機能について調べてみようと思い、ここにまとめてみました。 多分この記事は第1部として投稿され、追加分として2,3が次に投稿されていくことでしょう。 内容のソースとしては、弊社従業員や僕の友達で業界経験がある人に触りを教えてもらったって感じです。あとはこういう機能あったほうがいいかもなんて想像も混じったりしています。 本部機能とは? 各支部の売上管理機能 売上集計 売上分析 各支部の会員管理機能 会員集計 会員分析 商品などのマスタ管理機能 商品情報の登録 商品情報の配信 在庫管理機能 日報・月報閲覧機能 人件費閲覧機能 ドキュメント(マニュアル等)配信機能 以上がパッと思いつく限り挙げることができる機能でしょう。 とても多いですね 機能が多すぎてとてもわかりません。 そもそも、こういう機能を使用する人たちのニーズを考えるといってもそういった業界経験は全く無い僕です。 なので、今回は業界の業態と金勘定について調べてみました。 協力して頂いた方々には頭が上がりません。この場を借りて感謝いたします。 業態 POSレジと関連する本部機能と考えたら何を考えるでしょうか?僕はコンビニでした。 いろんな場所のコンビニのレジを一元管理する機能ですよね。 今回、業態を区別する際、商品マスタに焦点を絞ってみました。 結論から言って、大まかに4種類ありそうです。 支部所属\業態同業態異業態 自社のみスマレジ大手メーカー 他社含むコンビニショッピングセンター 以下、その特徴です。 自社-同業態 自社で同業態の支店を持っている業態。支店すべてが同業態なので、メニューや商品を統一することができるのが特徴。 店舗をまたぐ売上を集計分析できれば十分。つまり一般的なPOSシステム導入のみでほとんどのニーズをカバーできそう。 自社-異業態 自社で異業態(衣服・飲食・スーパー)の支店を持っている業態。支店によって取り扱う商品が違うのが特徴。 商品マスタを統一することが難しく、前支店で同じ商品マスタを使用するとなると商品管理がとても大変。だからそれぞれ違うマスタを用意し、業態ごとに導入する必要が出てくる。 売上分析も当然違ってくるので、それらすべてをまとめる本部機能が必要。 他社-同業種 支店のほとんどが違う法人から成り立つ業態。取り扱う商品はほとんど同じだが、法人(オーナー)が違うのが特徴 コンビニの様にオーナーを持ち、それを本部である本社がまとめる。 商品マスタは同業態なので統一することができるが、法人(オーナー)が違うので、レジはそれぞれが契約し、導入する必要がある。これを本部である本社が管理するためには契約をまたいで管理する機能が必要。更にロイヤリティの支払いを管理する機能が必要。 他社-異業種 支店のほとんどが違う法人から成り立ち、かつ扱う商品が違う業態。それぞれの法人がそれぞれの商品を1箇所に集まって商売をするのが特徴 アウトレットモールなどの商業施設のように、支部に全てオーナーが存在し、業態も衣類・飲食など雑多な店舗が一つの本部のもとで経営をしている形態。 契約をまたいで、売上を集計できる機能が必要で、更に利益の入金や、家賃や歩合の請求明細などを発行する機能が必要。 こう見ると・・・ 色々ありますね。これに当てはまらない例もありそうです。 例えば、フランチャイズについて調べていると、「コンビニ型フランチャイズとそれ以外」みたいに大別されている本もありました。 売上・利益の勘定方法 次は金銭の勘定方法に注目してみましょう。 特に、本部機能を搭載する上で一番仕様を詰めていくべきショッピングセンター(以下、SC)について着目し、纏めてみました。 前提 やはりこれは色々な形態の勘定方法があるらしい。 前提として、計算式そのものを利用者に決定される仕様が一番である可能性がある。 大まかに種別を分けることはできるが、どの形態にも例外はあるらしい。 例えば、百貨店における高級ブランドショップなどがそうらしい。百貨店側は購買者獲得のために、人気ブランドを百貨店内に設置したい場合がある。(定かでは無いが家電量販店のAppleStoreなどもそれに当てはまるのか??) このとき、百貨店は誘致することで人気ブランド店を設置しているらしい。つまり、百貨店側の利益に挙げる計算が他店舗と違う可能性が十分にありえるということです。 これを踏まえて種類を分けてみましょう。 家賃計算方法は坪単価が主流らしいが、これも時と場合によりけりかもしれません。 完全固定型 例:)不動産 売上に関係なく、設定された坪単価(土地の価格)×面積分が家賃となる。 とあるビルの一角を店舗として借り受け、毎月一定の「家賃」を支払う方法。ビルの管理者の収入となる。一番一般的。 完全歩合型 例:)百貨店 百貨店に属する支部に対して歩合率を決める。その店舗の売上×歩合率が家賃(みたいなもの)になる。 百貨店を経営する法人の収入となる。 併用型 例:)SC 坪家賃(坪単価×面積)と、(売上-最低保証)×歩合の合計が家賃となる。 家賃分は必ず支払う必要はあるが、売上には最低保障が設けられ、一定金額以上の売上が無い場合は免除になる。 ここで少し違う業態の勘定方法にも注目してみました。それはフランチャイズです。 フランチャイズの勘定方法 オーナーへ経営ノウハウを教えてもらったり、商品を利用したりして、自身で経営をしながら業界の勉強をしていくというのが、フランチャイズ業態の主な内容です。 特に、人気ブランドの看板を利用することができるというのは大きいですね。 しかし、その対価にロイヤリティという使用料金みたいなものを請求されます。 ではの請求金額の勘定方法について見てみましょう。 売上歩合型 一番一般的。多くの業種で利用されます。 売上✕歩合割 その理由は支部の利益とは関係なく一定のロイヤリティを得ることができるからです。 粗利型 特にコンビニ業界で採用され、他の業種ではあまり見ないそうです。 粗利✕歩合割 定額型 ロイヤリティ料金が定められていて、それを毎月支払います。 家賃みたいな勘定方法ですね。 まとめ 業態と金勘定についてまとめてみました。 こう見ると色々な計算方法や、色々な契約手法があるようです。 これらすべてを賄う機能を搭載する必要が出てきそうですね。 本部機能ですから特に狙うべきなのはショッピングセンターとフランチャイズかもしれません。 搭載すべきと思った機能はここには書けませんが、一番身近なコンビニやアパレルだけを勉強するとショッピングセンター用の機能が疎かになり、逆もまた然り。という状況に陥ってしまうかもしれません。 料金請求管理機能は十分な時間を書けて設計していきたいものです。 最後に 個人的に売上分析にBIツールを実装できればかなり使えるんじゃないかと考えています。 ただこれも実装するとなると、全体が肥大化してしまうような気も。。。 まぁ、できることからコツコツと! 今回は僕の勤務先に関わるネタでした! 本部機能に関わる記事はまた追加されていくことでしょう。その時はよしなに。 参考 弊社協力者と友人(本当にありがとうございました。)

テスト駆動はテストのための手法ではない

2019/07/15 2019/07/15

テスト駆動開発(TDD)は死んだって記事 正直、開発手法などの記事については上辺をすくっただけなので、よくわかりませんが、TDDは時代遅れの開発手法ということなのでしょうか? しかし、 @t_wada さんのセミナーに参加して以来、TDDが気になって仕方ありませんでした。(ライブコーディングは手法の有用性を知らしめるのにいい手段ということもセミナーで思い知りました。) 当時の僕はかなりのレガシーコードを修正していて、この本に書かれている通り、リファクタリングをする際にかなりの不安をもって修正をしていたからです。 セミナーの内容を見よう見まねで、個人開発プロジェクトでTDDしていましたが、この際しっかり勉強しようということで、本を購入し、この記事にまとめたいと思います。 前提 この記事は テスト駆動開発 を読んで書いた記事ではありますが、下記のような注意点があります。 この本の丸写しではない。つまり、僕の個人的価値観や経験をまじえて、僕が噛み砕いた内容を記載しています。 TDDそのものの知識を得るためにはこの本を読むか、関連するTDDの本を見ることをお勧めします。 ここでの僕の価値観とは この記事 でも書いたように ユーザー>コード 速度→正確性の順で重要(正確性を軽視せよというわけではない) と考えています。 もちろんこれに反論があるエンジニアの方もおられるかもしれませんが、これはTDDという開発手法ですので、合う合わないあると思いますし、これが正解とも言いません。 始めに 「TDDはテスト技法ではない。」これはまえがきにも書かれています。 TDDについて議論しているコミュニティを見ると、「自動テストは有用だが...」「テストを通すことが重要だ...」と書き込まれていることが少々あるが、これはTDDの議論ではないと僕は思います。 TDDとは分析技法であり、この分析を通じて開発をスムースに進める手法だと思っています。 だから、TDDの真価は「テストを書くことで製品の品質を担保するため」や、「テストを通すため」ではなく、「コーディングやリファクタリングで生じる欠陥と不安を出来るだけ排除するため」で、「品質の担保」や「テストのクリア」はTDDの副産物であると考えています。 簡単に言ってしまえば、 エンジニアの不安を取り除くための開発手法 ともいえるのではないでしょうか? まえがきで、天才的なプログラマーにとってはこの手法はとてもまどろっこしく、遠回りで面倒とあります。 優秀なプログラマは最初から正しく綺麗で、確実に動くプログラムを書けるのですから、当然でしょう。 テストを最初に書くということは、「失敗を前提」にしているということなので、優秀なエンジニアにとってもは無用のものです。 しかし僕はとてもじゃありませんが、天才ではありませんし、僕の書いたコードでバグを作ってしまうということは沢山ありました。 凡庸なプログラマである僕にとってTDDは「安心できる」上に「着実に前進できる」手法であると思っています。 開発サイクル とても有名な3つの単語を書きます。TDDのマントラだそうです。 レッド:動作しない、おそらく最初のうちはコンパイルも通らないテスト1つ書く。 グリーン:そのテストを迅速に動作させる。このステップでは罪を犯してもよい。 リファクタリング:テストを通すために発生した重複をすべて除去する。 繰り返しになってしまいますが、凡庸な僕の頭の中で幼稚に翻訳した内容を書きます。 一番始めにテストコードを書く。このテストを通すプログラムはまだ書かれていないので当然テストはエラーで終了する。 とりあえずテストを通すためのコードを書く。テストはクリアするが、実装が「とりあえず」なので、汚いコード 文字通り、リファクタリングする。汚い、つまりとりあえず書いたコードを綺麗な実装にする。 https://blog.ippon.tech/a-roadmap-to-tdd-adoption/ から引用 テストが意味するもの TDDのテストとは、もちろんテストコードの事を指しますが、テストを書くことで、仕様チェックをしているとも受け取れます。 テストコードを書くことで、思いつかない様な仕様バグを発見出来ます。 テストを書くということは仕様を満たすコードを書くということにつながるので、「仕様をプログラムコード」にするという作業になります。 少しだけ毛色が違いますが、 このサイト のおっしゃることも腑に落ちました。 レッド:仕様設計レビュー グリーン:探索テストレビュー リファクタリング:保守性レビュー http://kokotatata.hatenablog.com/entry/2015/03/05/120318 から引用 僕はTDDは開発手法であるといいました。この方もアジャイル開発手法としてTDDを利用しているみたいでした。 歩幅 この本には歩幅という単語が頻繁に出てきます。 歩幅というのはサイクルの粒度を指します。 期待する値、仕様を満たすテストを先に書き、とりあえず実装し、綺麗にしていく。この粒度は個人や開発部分によって粒度を変えるてよいとあります。 当然、1発でクリアしてもいいですし、難しい所ではコードを変更するたびにチェックしていくのもいいです。 例えば、ボールの個数をユーザーに均等に割り振って、そのあまりを返すロジックがあったとします。 この時、テストはこのようになるでしょう。 copied./** * test getRemainder method */ public function testGetRemainder() { $sample = new Sample(3, 2); $this->assertEquals(1, $sample->getRemainder()); } この実装は簡単なので、経験のある方ならすぐにゴールまで行けるでしょう。サイクルなど必要ありません。 copied.class Sample { protected $ballNum; protected $userNum; public function __construct($ballNum, $userNum) { $this->$ballNum = $ballNum; $this->$userNum = $userNum; } public function getRemainder() { return $ballNum % $userNum; } } 上記のようにすぐにゴールまで、たどり着くことができるでしょう。 簡単な実装なので、エンジニアは不安も抱きませんし、仕様も簡単なので、答え合わせも簡単です。 この状態が歩幅が大きいと言えます。ゴールまで1歩でクリアしていくということですね。 仮にこの処理が実装の難しい箇所と想定したときの話をしましょう。 実装が難しく、エンジニアは不安を抱えています。仕様も理解しやすいとは言い難く、所謂「仕様がふわふわしている」状態です。 ここの歩幅は細かくするといいでしょう。 サイクルを頻繁に回す、つまり歩幅を小さく細かく実装していくことで、チェックを頻繁にし、エンジニアの不安を解消し、仕様の理解を助けます。 copied./** * test getBallNumPerUser method */ public function testGetBallNumPerUser() { $sample = new Sample(); $this->assertEquals(1, $sample->getRemainder()); } とりあえずテストを実行してみましょう。 この状態でテストを流すと、SampleクラスもgetRemainderメソッドも存在しないので、エラーが発生します。 これがレッドですね。 これをすぐに解消するために、中身を実装しましょう。 一歩目は下記のように実装しました。 copied.class Sample { public function getRemainder() { return 1; } } テストクリアです。 とりあえずプログラムは動作します。 しかし、こんなプログラムに意味はありません。 有用な処理にするためにリファクタリングしましょう。 ボールを均等に配布した結果、あまりの数を出力するのですから、ボールの全ての個数をユーザー数で割り算し、そのあまりを出力すればいいですね。 copied.class Sample { public function getRemainder() { return 3 % 2; } } テストクリアです。 とりあえずプログラムは動作します。 しかしこれではボールの数が3つ固定で、ユーザーの全体数も2で固定されています。 これも有用とは言えません。 もう一度リファクタリングしましょう。 ボール数とユーザー数を格納する変数を用意したらいいですね。 copied.class Sample { protected $ballNum = 3; protected $userNum = 2; public function getRemainder() { return $this->ballNum % $this->userNum; } } テストクリアです。 しかしこれでは固定値の問題をクリアできていません。 コンストラクタで値を設定してあげましょう。 copied.class Sample { protected $ballNum; protected $userNum; public funcion __construct($ballNum, $userNum) { $this->ballNum = $ballNum; $this->userNum = $userNum; } public function getRemainder() { return $this->ballNum % $this->userNum; } } テストクリアです。 可変数で計算結果を取得できるようにもなりましたし、リファクタリングはここまでとしておきましょう。 (もちろん、入出力バリデーションなど、バグを生む実装ですが、ここではあえて触れていません。気になる方はTDDで続きを実装しても面白いかもしれませんね。) これで最初に書いた実装と同じ実装になりましたね。 ここで何が言いたいかというと、 個人の技量 設計の複雑さ 実装時間の制約 さまざまな理由によって歩幅は変えることができるということです。 TDDが受け入れがたい方の意見の1つにこんなものがあります。 なにか実装を変更するたびに、後ろを振り返ってチェックする。 しかも大量に実装されるであろうテストコードをだ。 これは時間の無駄だし、効率が悪い。 僕もそう思います。 大きなプロジェクトになってくるとテストを流すだけで、何十分、何時間と取られますし、明らかにクリアできるコードをテストするのは効率が悪いです。 しかし、TDDは歩幅の調節ができます。 明らかに無駄、明らかにテストクリアできる実装だとわかっている場合は歩幅を大きく取ってしまえばいいのです。 TDDはエンジニアの不安を取り除く開発手法です。 テストをクリアするためのものではありませんし、品質を担保するものでもありません。 不安がなければテストしなければいいのです。 不安が出てくれば歩幅を小さくとって、何回も振り返り、チェックをしましょう。 テストと「対話」する TDDとの大きなメリットを感じる1つの考え方です。 これは本に書いてあったか忘れてしまいましたが、僕はTDDはテストと「対話」する開発手法だと考えています。 不安を感じたらテストと相談する プロジェクトに限らず、何かに不安を感じたら、人は他人に相談することでしょう。 それと同じように、仕様や実装に不安を感じたらテストに聞いてみましょう。 テストコードの実装に苦労したり、テストコードの実装中に明らかに仕様バグを生む設計である可能性を消すことができます。 テストを一度書いたらそれでFixでは無い 前章で「歩幅」について書きました。テストコードを書いて、実装します。このサイクルが細かいというのは、機能毎という意味もありますが、1機能単位でもありえます。 テストコードを書き実装した結果、 テスト内容が未熟であることに気づく 該当箇所のテストコードを修正している間、該当箇所の仕様バグを発見 仕様バグを修正する実装 ・・・ 僕がTDDで実装していると、上記の感じで実装の進捗とともにテストコードもだんだん良くなっていく現象がありました。 この現象がとても良く、テストコード実装=仕様確認をしている時間となります。 設計書とにらめっこしているよりもよほど有意義な時間となりました。 実際の工数について 工数は明らかに増える 工数は明らかに増えます。2倍くらいになります。当然です。テストコードは仕様の実装確認を示すコードですから実装分だけテストコードも増えます。 しかし、体感工数は少なくなります。 体感工数=ストレス 体感工数とはストレスだと考えています。 明らかに物理時間が伸びているのに何故かというと、「出戻り」が格段に減っています。 仕様確認をテストコードを書く時間に行っているため、 実装する前に仕様的なバグなどに気づくことができます 従来の方法で実装していると、実装してから気付いて、また実装・・・という悪循環です。 TDDの場合は、それは限りなく少なくなるので、実装時間が伸びますが、その分「確実に進捗が出ます」。 これはエンジニアのメンタルに非常に優しいです。 まさに「エンジニアの不安を取り除くための開発手法」といえます。 最後に 最後の方は少し駆け足気味になってしまいましたが、僕のTDDに対する感情をつらつらと述べてみました。 実務的な記事も今後書いていこうと思いますが、正直 個人によってTDDが合う、合わないはある と思います。 皆さんも一度お試しになって、実務で導入するかどうかを決めたほうが良いかもしれませんね。 .

MySQLでの高速化の基礎①

2019/07/05 2019/07/05

SQLの高速化の基礎 こんにちは。Nonです。今回はありそうでなかったSQLの高速化の基礎について記載します。 実は今まで触れる機会が少なかった 個人的にSQLが遅くて困ったことが無い(規模の小さい商品や、個人開発などのみかかわってきたもので・・・)ので、知らないこと多かったんですよね。 僕の経験で言えば、多くても5万くらいのデータしか扱ってきませんでした。 実はもっとデータの量もあったかもしれないけど、それは先人たちがすでに高速化していたものを使いまわしていただけだったと思います。 最近、データの量が多い画面を担当することになって、この知識があるかないかで、ユーザーへの便利度が決まるということに気づきました。 なので、備忘録ついでに書いておくことにしました。 EXPLAIN 高速化といえばまずこれ。インデックスが適切に使用されているかなど、MySQLがどのように実行するかの情報を見ることができます。 copied.EXPLAIN SELECT * FROM users WHERE user_id = 1 typeを確認 type内容 constPRIMARY KEYまたはUNIQUEインデックスのルックアップによるアクセス。最速。 eq_refJOINにおいてPRIARY KEYまたはUNIQUE KEYが利用される時のアクセスタイプ。constと似ているがJOINで用いられるところが違う。 refユニーク(PRIMARY or UNIQUE)でないインデックスを使って等価検索(WHERE key = value)を行った時に使われるアクセスタイプ。 rangeインデックスを用いた範囲検索。 indexフルインデックススキャン。インデックス全体をスキャンする必要があるのでとても遅い。 ALLフルテーブルスキャン。インデックスがまったく利用されていないことを示す。OLTP系の処理では改善必須。 インデックス 高速化といえばまずこれ。 簡単に「インデックス」といいますが、結構扱いに困るイメージです。 知っているようで知らない感じに陥りそうな困ったちゃんです。 単純なインデックス copied.SELECT * FROM articles WHERE id = 1 idは当然PRIMARY_KEYでAUTO_INCREMENTなので、検索が早いです。 イメージとしては辞書の「あ/か/さ/た/な/は/ま/や/ら/わ」の索引のイメージ。 範囲を扱うインデックス copied.SELECT * FROM articles WHERE post_datetime BETWEEN '2019-04-01' AND '2019-05-01' AND user_id = 1 このときはpost_datetimeとuser_idに複合インデックスを作成しておくと早くなる。 でもこの例では一つだめなところがある。 インデックスの順番 post_datetimeで検索するよりも先にuser_idで検索される箇所から検索したほうが早い。 copied.SELECT * FROM articles WHERE user_id = 1 AND post_datetime BETWEEN '2019-04-01' AND '2019-05-01' 正しくはこう。 post_datetimeはrange検索なので、あとから検索したほうが早くなる。 インデックスを貼る順番にも注意。 user_id post_datetime だ。 インデックスを使わない選択肢 copied.SELECT * FROM memo WHERE delete_flag = false メモの削除件数0件。 このとき、インデックスがあろうがなかろうがフルスキャンである。 こういったインデックスの恩恵を受けられないカラムにはインデックスを貼らない方がいい。 インデックスを貼ると、INSERTの負荷が重くなるからだ。 (adsbygoogle = window.adsbygoogle || []).push({}); JOIN JOINするときはできるだけ絞れ。 JOINはループ 下記は例。(いい例が思いつかずすみません。) copied.SELECT * FROM articles LEFT JOIN comments ON comments.articles_id = articles.id AND comments.articles_id = 1 WHERE articles.id = 1 JOINする対象テーブルは予め小さくしておく。 AND comments.articles_id = 1の箇所がそうです。 JOINをするときMySQLは内部的にループを回して条件を絞り込む。 例えば、ON comments.articles_id = articles.idの箇所、あっているものをループで検索しだす。ということは予めループされるものを小さくしておけば、検索までにかかる時間は短くなる。 phpで例を挙げると copied.// join処理① for ($i = 0; $i < 1000; $i++) { for ($j = 0; $j < 1000; $j++) { } } // join処理② for ($i = 0; $i < 100; $i++) { for ($j = 0; $j < 100; $j++) { } } ①より②の方が早いに決まっている。 EXISTSという選択肢 copied.SELECT * FROM articles WHERE EXSITS ( SELECT 1 FROM comments WHERE comments.articles_id = articles.id ) EXISTSは存在を確認するのみなので、早い。デメリットとして、EXSITSで書いたテーブル情報を参照することはできない。例ではコメントの有無はわかるが、コメントの内容を参照できない。 その他 数件程度ならパフォーマンス的にも問題が無いが件数が増えていくほどに効果が増していく系。 ちゃんとバッククォート「`」を記載する。 copied.SELECT * FROM `articles` WHERE `articles`.`id` = 1 指定された型を意識する copied.SELECT * FROM `users` WHERE `name` = '007' /* 007という名前でも検索は文字型で */ 最後に 疲れた・・・ いざ書き出したらキリがないですね。これ。 記事をあとから更新するか、分割するかします。お疲れ様でした。 また更新しますのでタグ検索など利用して見てください。