フレームワークや開発ツールを取り入れることの意義とは

ここ数年、JavaScript フレームワークの盛り上がりを感じます。いろいろと適用事例を聞く機会が増えてきました。

が、最近、JavaScript フレームワークを用いた SPA をやめて、サーバーサイドレンダリングに戻したという話をいくつか聞きました。

なんとなく、揺り戻し のような現象が起きているように思えます。

なぜそういうことが起きるのか、自分なりの考えを簡単にまとめてみます。もちろん、すべてのケースに当てはまるわけではないですが。

そもそも、フレームワーク (アプリケーションフレームワークや開発手法など) や開発ツールを取り入れる際、それによって解決したい問題や課題はなにか を明確にすることが重要です。

で、冒頭で挙げた近年の JavaScript フレームワーク事情においては、「新しい技術である」「流行りの技術である」という理由で「とりあえずこれを使ってみよう」というような短絡的な判断を下しがちになっているように思えます。もちろん、流行りに追従して新しい技術をおさえることは大切ですし、世間でいろいろ取り沙汰される故、焦りを覚えることもあると思います。私もそうです。

ただ、ここでよくないと思うのは、いわゆる 手段が目的になっている ような状態です。

しかし、新しいことを取り入れた直後というのは、得てして上手くいっているように見えるものです。不思議なことに。ところが、本来解決するべき問題や課題を明確にしていないと、いずれは頓挫して上手くいかない状況に陥り、方針を見直す必要性が出てきたりするものです。これが揺り戻しの正体ではないかと考えています。(とはいえ、方針を見直したところで実際には移行できないことが多いかもしれませんが...)

解決したい問題や課題はなにか、普段、開発や運用をする上で不安に感じていることはなにか、どうやったらその不安を取り除けるのか、このあたりをあらかじめ明確にしておくと、どのようなフレームワークや開発ツールが相応しいのか、正しい判断ができるようになると思います。もしかしたら、全体適用ではなく部分適用で十分かもしれないし、いくつかの手法を組み合わせて取り入れることでよりよい効果を生み出せることがあるかもしれません。

また、このようなプロセスを経て改善したものは、メンテし易く、長期に亘って効果を生み出すものだと思います。これは、理に適った改善であり、本来解決するべき問題や課題が解決されるためです。

このように、手段ではなく目的を中心に据えて、解決するべき問題や課題を解決することによって不安を取り除くことが、フレームワークや開発ツールを取り入れることの意義である、と考えています。

現場からは以上です。

Eclipse の Team Synchronizing パースペクティブが開かない

Eclipse でプロジェクトのメニューから Team > Synchronize with Repository とすると、今まで Team Synchronizing パースペクティブが開いていたが、新しい開発環境を構築したら、ふとした拍子にパースペクティブが開かなくなってしまった。そんなときのメモ。

※ Mars.2 Release (4.5.2)

  1. Window > Preferences > Team
  2. Open the associated perspective when a synchronize operation completesAlwaysPrompt をチェックする

Never がチェックされているとパースペクティブではなくビューとして開く。

おそらく、最初にプロンプトが出たときに間違えて Remember my decision をチェックして No をクリックしたと思われる。そうすると Never で設定される模様。まぁ、ビューでもいいんだけど、パースペクティブの方が見慣れているので。

GitHubとクラスメソッドの勉強会に行ってきた #github_method

GitHubとクラスメソッドの勉強会に行ってきました。簡単に所感をまとめます。

classmethod.connpass.com

今回のテーマは「GitHub x AWS の最新 DevOps 事情」です。

  • DevOps はインフラ自動化やリリース自動化など、ひとによって捉え方は違う
  • 価値を無駄なく遅滞なく届けることが重要
  • かつてはスコープに対してスケジュールやリソースを決めていた
  • 近年はこの関係が逆転して、スケジュールとリソースがある上でスコープを決める
  • VCSによるコラボレーションをやりやすくする
    • 中央集権型から分散型へ
  • CI は必須のベストプラクティス
  • Continuous Delivery : 継続的にデリバリーする
  • Continuous Deployment : 継続的にデプロイする
  • GitHub の開発者は世界中にいる
    • それでも1日のデプロイは100回に及ぶことも
    • それを支えるのは GitHub FlowHubot (ChatOps)
  • master を常にデプロイ可能に保つ (本番とイコール)
    • 本番にデプロイして問題がないものを master にマージする
      1. master から feature を作ってコミットを加える
      2. PR を作って開発者みんなでレビューする
      3. レビューで問題がなければ PR ブランチを本番にデプロイする
      4. 本番で問題がなければ master にマージする
    • 本番にデプロイした PR ブランチに問題があれば master をデプロイする
  • master にマージしてからデプロイすると問題があった場合に master が不安定な状態になってしまう
  • 1回本番にデプロイされると、問題ないことが保証されるまでは次以降のデプロイはロックされてキューイングされる
  • Chat は Shared Console である
    • オープンなコンソール (みんなで見ることができる)
    • 誰がやっても同じ結果になる
    • ゆくゆくは AI が絡んでくるかな?

GitHub Flow は2011年頃に出てきたものですが、中のひとたちは割と早い段階で今の運用に変えていたようです。当時、日本にはそのあたりの情報があまりなかったため、池田さん自身もチーム開発実践入門を執筆した段階ではご存知なかったらしいです。
また、1回でデプロイされる変更ボリュームは数十行程度。敢えて小さくしている。1日のトータルは3000行くらいだが、一気にデプロイするのではなく、30行を100回に分けてデプロイするような感じ。とのこと。

www.slideshare.net

  • DevOps の目的はビジネスに使う時間を増やすこと
    • 決まりきった運用や手運用を自動化する
    • 障害対応の時間を減らす
  • CodeDeploy ではデプロイとプロビジョニングを混ぜないように注意
    • プロビジョニングは OS やミドルウェアのための設定
    • プロビジョニングとデプロイではライフサイクルが違う
    • CodeDeploy ではプロビジョニングはしない
  • AWS を使うならマネージドサービスを使うことを第一に検討する
    • 安定性やサービス連携、運用負荷を減らす観点から
    • 標準のサービスでは要件に合わない場合には自前で作る
  • GitHub Enterprise を使う場合は標準で提供している設定や機能を使う
    • アップデートで /data/user 以外が上書きされるため
    • 運用負荷を減らす

AWS については圧倒的経験不足のため、CodeStar をはじめ、多くの DevOps 関連のサービスが提供されていることを初めて知りました...。

正規表現メモ

CODEPREP で正規表現を学びつつ、練習問題の3パターンをメモる。

codeprep.jp

電話番号にマッチさせる

  • 電話番号は3ブロックの数字の並びで構成される
  • 各ブロックの桁数は 3桁、3桁 or 4桁、4桁
  • 各ブロックは - または ( or ) で区切られる
  • 先頭のブロックの数字は0で始まる
  • 先頭または末尾に空白や数字以外があることは許容しない
/^0\d{2}[-(]\d{3,4}[-)]\d{4}$/

URLからホスト名を抽出する

  • URLは http:// または https:// で始まる
  • ホスト名の後ろに / がない場合もある
/https?.{3}([^/]*)/

http:// または https:// で始まるということは、http の後ろに s があってもなくてもよいということと同義である。正規表現では https? で表す。

ホスト名は、http(s):// 以降で最初に現れる / より前の部分を抽出する。つまり、http(s):// 以降で / 以外の文字が続く限りキャプチャする。正規表現では ([^/]*) で表す。

HTML要素の属性値を抽出する

  • a 要素から href 属性の値をキャプチャする
  • = の前後には空白が入ることがある
  • 属性値は " または ' で括られる
  • href 以外の属性が定義されることがあり、順序は不定である
/<a\s*.*href\s*=\s*(["'])(.*?)\1.*/

空白があるかないかは、正規表現では \s* で表す。

シングルクォートとダブルクォートの両方に対応する場合は ["'] で表す。また、後ろのクォートは前のクォートと同一である必要があるため、前のクォートをキャプチャして \1 で後方参照する。キャプチャを複数定義するため、抽出結果は配列の2番目(\2)を取る。

href 以外の属性が存在し得るため、同じクォートが複数回現れることがある。キャプチャは (.*?) で最短マッチにする。

Cookpad Tech Kitchen #7 に行ってきた #cookpad_tech_kitchen

Cookpad Tech Kitchen #7 に行ってきました。簡単に所感をまとめます。

cookpad.connpass.com

今回のテーマは『理想の開発現場の「ふつう」のお話』です。資料はこちら。

  • 開発者もテストエンジニアも品質を向上するという共通の目的を持つ
    • テストの現場の理想形のひとつ
  • 日々の小さな会話がよいチームを作るのではないか
    • フレーズ→振る舞い→価値観
  • 「うまくいったらどうなるの?」
    • ゴールまでの仮説を立てる
    • うまく迷える (ゴールまでのステップが分かっていれば迷っても安心)
  • 「なんでやるんだっけ?」
    • 作業の目的や作業自体の意味を考える
    • やりすぎを抑える
  • 「やりたくないの?」

    • 「やりたいくない」がチームに認められる
    • やりたくないことをチームで共有する
  • 「なんでやるの?」という問答は1対1かもしれない

    • その場にいるみんなが考えるきっかけになる
    • それぞれの認識にズレがあるのかないのかを知ることができる

「なんでやるの?」はやりすぎを抑えられる、というのはよく理解できます。何かに没頭したり集中するあまり物事の優先度の意識が薄れて、つい本質から外れたり、ときには手段が目的になっていたりすることがあります。そういうときに「なぜやるのか?」を自身に問いかけることで、目的を再認識して軌道修正をかけることができると思います。

今回、「明日から使える」というのが登壇者のテーマだったようです。で、実際にすぐに現場に適用できるかどうかというと、当然現場の状況にもよりますが、大半は難しいかも、というのが正直な印象です。

どなたかの質問でも話が挙がりましたが、今回の話のような内容がチームのメンバーに対して効果を発揮するのは、ある程度完成されているチームに限るような気がします。

事業会社では必然的に似たような目的意識を持ったメンバーが集まるのかもしれませんが、受託開発を行う会社では多様なメンバーでチームが構成されることが多いかと思います。(偏見かもしれませんが...)
当然、メンバー全員が当事者意識を持っているかというと決してそうではないだろうし、そのようなチームに対してどのような振る舞いがよい作用をもたらすか、ということを一様に述べることはなかなか難しいのではないかと思います。そのようなチームでは、まずは目的意識を共有できるようにチームの「風土作り」から始める必要があると考えます。しかし、今回はそのあたりは前提から外れているように感じました。

理想の開発現場の「ふつう」のお話というテーマでしたが、そこで「ふつう」に行われていることは、多くの「理想とは程遠い開発現場」にとっては他でもない『理想』そのものであるわけです。

ただまぁ、こういう話をきちんと自分の中で腹落ちさせてチームに共有するところから始めることが、よいチームの風土作りの第一歩になるんだろうと思います。そういう意味でいろいろな気付きが得られたとてもいい機会でした。

Haskell を使ってみる 7 (ガード)

前回の続き

Haskell を使ってみる 6 (パターンマッチ) - kntmr-blog

ガード

ガードは引数の値が満たす性質で処理を分岐させるときに使う。パイプ文字 (|) と条件式と関数本体を組み合わせて記述する。(パターンは引数の構造で条件を分岐させるもの)

-- 階乗を求める関数
fact :: Integer -> Integer
fact n
    | n < 0 = error "error!!"
    | n == 0 = 1
    | otherwise = n * fact(n - 1)

*Main> fact 3
6

where

where キーワードは計算結果を変数に束縛するときに使う。where で定義した変数のスコープはその関数内のみ。

-- where キーワードを使わない場合
totalCheck :: Integer -> Integer -> String
totalCheck x y
    | x + y < 10 = "less than 10"
    | x + y < 100 = "less than 100"
    | otherwise = "a large number"

-- where キーワードを使う場合
totalCheck' :: Integer -> Integer -> String
totalCheck' x y
    | total < 10 = "less than 10"
    | total < 100 = "less than 100"
    | otherwise = "a large number"
    where total = x + y

where キーワードの中でパターンマッチを使うことができる。

initials :: String -> String -> String
initials firstname lastname = [f] ++ ". " ++ [l] ++ "."
    where (f:_) = firstname
          (l:_) = lastname

let

let 式では関数のどこでも変数を束縛することができる。let 式自身が式であり、変数の束縛は局所的でガード間で共有されない。(where キーワードは関数の終わりで変数を束縛する)

let で変数に束縛し、続く in に式を記述する。

-- ローカルスコープに関数を作ることができる
Prelude> [let square x = x * x in (square 2, square 3, square 4)]
[(4,9,16)]

-- セミコロン区切りで複数の変数に束縛できる
Prelude> (let a = 10; b = 20; c = 30 in a * b * c, let foo = "Hello"; bar = "Haskell" in foo ++ " " ++ bar)
(6000,"Hello Haskell")

-- let 式とパターンマッチでタプルを要素に分解して変数に束縛できる
Prelude> (let (a, b, c) = (1, 2, 3) in a + b + c) * 100
600

リスト内包表記と let 式の組み合わせ。述語のように使っているが、フィルタしているのではなく計算結果を変数に束縛している。

-- リストから値を受け取り、計算結果を area に束縛する
calcCircleArea :: [Double] -> [Double]
calcCircleArea xs = [area | r <- xs, let area = r * r * 3.14]

case

case 式ではコード中のどこでもパターンマッチを使うことができる。変数で指定した値に基づいてコードブロックを評価する。case 式は関数の引数に対するパターンマッチと似ている。(実際、case 式の糖衣構文になっている)

case 式に合致するパターンが見つからない場合はランタイムエラーが発生する。

-- 関数の引数に対するパターンマッチ
head' :: [a] -> a
head' [] = error "empty list"
head' (x:_) = x

-- case 式
head'' :: [a] -> a
head'' xs = case xs of [] -> error "empty list"
                       (x:_) -> x

引数によるパターンマッチは関数定義のときしか使えない。case 式では、式の途中でパターンマッチを使うことができる。

checkList :: [a] -> String
checkList list = "This list is "
               ++ case list of [] -> "empty."
                               [x] -> "a singleton list."
                               xs -> "a longer list."

-- case 式の代わりに where を使う場合
checkList' :: [a] -> String
checkList' list = "This list is " ++ what list
    where what [] = "empty."
          what [x] = "a singleton list."
          what xs = "a longer list."

今回はガードのあれこれについて。

Haskell を使ってみる 6 (パターンマッチ)

前回の続き

Haskell を使ってみる 5 (型) - kntmr-blog

パターンマッチ

パターンマッチは、データ型が従うべきパターンを指定してそのパターンに従ってデータを分解するために使う。

-- パターンマッチと再帰で n の階乗を求める関数
factorial :: Int -> Int
factorial 0 = 1
factorial n = n * factorial (n - 1)

*Main> factorial 5
120

パターンマッチでパターンに合致しない値を指定するとエラーになる。パターンマッチの最後にすべてに合致するパターンを入れる。

showNumber :: Int -> String
showNumber 1 = "One"
showNumber 2 = "Two"
showNumber 3 = "Three"

*Main> showNumber 1
"One"
*Main> showNumber 5 -- これはパターンにない
"*** Exception: pattern-matching.hs:(6,1)-(8,22): Non-exhaustive patterns in function showNumber
タプルのパターンマッチ

タプルの要素を分解して処理できる。

-- ペアを受け取って足し合わせる関数
addVectors :: (Int, Int) -> (Int, Int) -> (Int, Int)
addVectors (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)

*Main> addVectors (1, 2) (2, 3)
(3,5)
リストのパターンマッチ

リスト内包表記でパターンマッチを使う場合、パターンに合致しないものは処理されない。

Prelude> let xs = [(1,2), (2,3), (2,4), (1,5), (3,6)]
Prelude> [x * 100 | (1, x) <- xs] -- 1つ目の要素が1のペアのみ処理される
[200,500]
リストのパターンマッチ

リストの先頭3要素を足し合わせる関数。

sumThree :: (Num a) => [a] -> a
sumThree [] = 0
sumThree (x:[]) = x
sumThree (x:y:[]) = x + y
sumThree (x:y:z:[]) = x + y + z
sumThree (x:y:z:_) = x + y + z -- この行がないと4要素のリストを指定した場合にエラーになる

*Main> sumThree []
0
*Main> sumThree [1]
1
*Main> sumThree [1, 2]
3
*Main> sumThree [1, 2, 3]
6
*Main> sumThree [1, 2, 3, 4] -- 4つ目の要素は足し合わされない
6
as パターン

as パターンでは、パターンマッチの対象になる値自体を参照することができる。パターンの前に名前と @ を付ける。

firstLetter :: String -> String
firstLetter "" = "Emptry string."
firstLetter all@(x:xs) = "The first letter of " ++ all ++ " is " ++ [x]

*Main> firstLetter ""
"Emptry string."
*Main> firstLetter "Hello"
"The first letter of Hello is H"

今回はパターンマッチについて。